Data Downloading

sratools

We downloaded the data using SRAtools on grace using the scripts generated by the following codes. We downloaded the sratools from github and installed it on grace with setup. All files downloaded were not zipped so we also zipped each file into fastq.gz file.

id.list<-readLines("/Users/yanxiting/Downloads/SRR_Acc_List(2).txt")
temp1<-paste("/home/xy48/scratch_palmer/sratoolkit/sratoolkit.3.0.0-ubuntu64/bin/prefetch.3.0.0 -X 9999999999999 ",id.list,"\n",sep="")
temp2<-paste("/home/xy48/scratch_palmer/sratoolkit/sratoolkit.3.0.0-ubuntu64/bin/fastq-dump.3.0.0 --split-files ./",id.list,".sra\n",sep="")
temp3<-rep("cd /home/xy48/scratch_palmer/SARC_10x/test/sra\n",length(temp1))
temp4<-paste("gzip ./",id.list,"_*.fastq\n",sep="")
temp5<-rep("ssh transfer\n",length(temp1))
cmd.out<-cbind(temp5,temp1,temp3,temp2,temp4)
temp.cmd<-apply(cmd.out,1,paste,collapse="",sep="")
cat(temp.cmd,file="./download_commands.txt",append=F,sep="")

changing names

The cell ranger pipeline recognize fastq file names are formated as [Sample Name]S1_L00[Lane Number][Read Type]_001.fastq.gz. There are 3 files per sample representing I1, R1 and R2 downloaded by sratools, which were named as *_1, *_2 and *_3.fastq files. To correctly run cell ranger on these files, we need to change the fastq.gz names.

# this was run on grace.
source("~/Rprogram/my_functions.R")
data.dir<-"/home/xy48/scratch_palmer/SARC_10x/test/sra"
filenames<-list.files(data.dir)
temp<-sapply(filenames,my.element.extract,splitchar="\\.",index=3)
filenames<-filenames[temp=="gz"]
filenames<-filenames[!is.na(filenames)]

for(i in 1:length(filenames)){
  
  if(substr(filenames[i],1,4)=="SRR9"){
    from.filename<-filenames[i]
    temp<-my.element.extract(filenames[i],splitchar="\\.",index=1)
    temp<-my.element.extract(temp,splitchar="_",index=2)
    if(temp=="1"){
        to.filename<-paste0(my.element.extract(filenames[i],splitchar="_",index=1),"_S1_L001_I1_001.fastq.gz")
    }
    if(temp=="2"){
        to.filename<-paste0(my.element.extract(filenames[i],splitchar="_",index=1),"_S1_L001_R1_001.fastq.gz")
    }
    if(temp=="3"){
        to.filename<-paste0(my.element.extract(filenames[i],splitchar="_",index=1),"_S1_L001_R2_001.fastq.gz")
    }
  
    file.rename(from=file.path(data.dir,from.filename),to=file.path(data.dir,to.filename))
    cat("from=",file.path(data.dir,from.filename),"\n","to=",file.path(data.dir,to.filename),"\n",sep="")
  }else{
    
    from.filename<-filenames[i]
    temp<-my.element.extract(filenames[i],splitchar="\\.",index=1)
    temp<-my.element.extract(temp,splitchar="_",index=2)
    if(temp=="1"){
        to.filename<-paste0(my.element.extract(filenames[i],splitchar="_",index=1),"_S1_L001_R1_001.fastq.gz")
    }
    if(temp=="2"){
        to.filename<-paste0(my.element.extract(filenames[i],splitchar="_",index=1),"_S1_L001_R2_001.fastq.gz")
    }
    if(temp=="3"){
        to.filename<-paste0(my.element.extract(filenames[i],splitchar="_",index=1),"_S1_L001_I1_001.fastq.gz")
    }
  
    file.rename(from=file.path(data.dir,from.filename),to=file.path(data.dir,to.filename))
    cat("from=",file.path(data.dir,from.filename),"\n","to=",file.path(data.dir,to.filename),"\n",sep="")    
    
  }
}

cell ranger

We rsync the fastq.gz files onto farnam under ~/scratch60/SARC_10X/fastq and run cell ranger on the files using commands generated by the following codes.

id.list<-readLines("/Users/yanxiting/Downloads/SRR_Acc_List(2).txt")
temp1<-paste("#!/bin/bash\n#SBATCH --time=24:00:00  --ntasks=1 --partition=general --cpus-per-task=8 --mem=80GB --job-name=",id.list," -o /home/xy48/scratch60/SARC_10X/cellranger_scripts/",id.list,".sh.o%J -e /home/xy48/scratch60/SARC_10X/cellranger_scripts/",id.list,".sh.e%J\n",sep="")
temp2<-rep("module load cellranger/5.0.0\nmodule load bcl2fastq2/2-20-0-foss-2018b\n",length(id.list))
temp3<-paste("cd /home/xy48/scratch60/SARC_10X/cellranger_results\n")
temp4<-paste("cellranger count --id=",id.list," --fastqs=/home/xy48/scratch60/SARC_10X/fastq --transcriptome=/home/xy48/scratch60/SARC_10X/refdata-gex-GRCh38-2020-A --localcores=8 --localmem=60 --sample=",id.list,"\n",sep="")
cmd.out<-cbind(temp1,temp2,temp3,temp4)
temp.cmd<-apply(cmd.out,1,paste,collapse="",sep="")
cat(temp.cmd,file="./cellranger_scripts.txt",append=F,sep="")

There are samples with multiple runs. We run cellranger aggr to merge the multiple runs into one single nUMI vector.

source(file.path(home.dir,"Rprogram/my_functions.R"))
library(xlsx)
home.dir<-"/home/yanxiting/driver_Farnam"
runtable.filepath<-file.path(home.dir,"scratch60/SARC_10X/SraRunTable.txt")
id.filepath<-file.path(home.dir,"scratch60/SARC_10X/scRNA_ids.xlsx")

run.table<-read.table(runtable.filepath,sep="\t",header=T,check.names = F)
id.table<-read.xlsx(id.filepath,sheetIndex = 1,check.names=F)

temp.table<-run.table[run.table$`GEO_Accession (exp)`%in%as.matrix(id.table)[,1],]
temp.list<-split(as.matrix(temp.table)[,1],temp.table$`GEO_Accession (exp)`)

# extract the samples with multiple runs
temp.list<-temp.list[unlist(lapply(temp.list,length))>1]

# find out the fastq part these IDs belong to
temp.filenames<-list.files(file.path(home.dir,"scratch60/SARC_10X"))
temp.filenames<-temp.filenames[grep(".filelist",temp.filenames)]
temp.filenames<-temp.filenames[grep("fastq",temp.filenames)]

file.list<-list()
for(i in 1:length(temp.filenames)){
  temp<-readLines(file.path(home.dir,"scratch60/SARC_10X",temp.filenames[i]))
  temp<-unname(sapply(temp,my.element.extract,splitchar="/",index=-1))
  temp<-unname(sapply(temp,my.element.extract,splitchar="_",index=1))
  temp<-unique(temp)
  file.list[[i]]<-temp
}
names(file.list)<-temp.filenames
temp<-character()
for(i in 1:length(file.list)){
  temp<-c(temp,rep(names(file.list)[i],length(file.list[[i]])))
}

file.list.matrix<-cbind(unlist(file.list),temp)

temp<-list()
for(i in 1:length(temp.list)){
  temp[[i]]<-unique(file.list.matrix[file.list.matrix[,1]%in%temp.list[[i]],2])
}

# transfer the cellranger results back to farnam
# generate the aggregation CSV file
cellranger.dir<-file.path(home.dir,"scratch60/SARC_10X/cellranger_results")
output.dir<-file.path(home.dir,"scratch60/SARC_10X/cellranger_aggr_scripts")

for(i in 1:length(temp.list)){
  output.filepath<-file.path(output.dir,paste(names(temp.list)[i],"_aggr.csv",sep=""))
  # pipeline before cellranger 6.0, use library. Otherwise, use sample_id
  cmd.out<-c("library_id","molecule_h5")
  cmd.out<-rbind(cmd.out,cbind(temp.list[[i]],paste("/home/xy48/scratch60/SARC_10X/cellranger_results/",temp.list[[i]],"/outs/molecule_info.h5",sep="")))
  write.table(cmd.out,file=output.filepath,row.names=F,col.names=F,sep=",",append=F,quote=F)
}

# generate the sh file to run cellranger aggr on the replicated runs of the same sample.
script.dir<-file.path(home.dir,"scratch60/SARC_10X/cellranger_aggr_scripts")
result.dir<-file.path(home.dir,"scratch60/SARC_10X/cellranger_aggr_results")

for(i in 1:length(temp.list)){
  csv.filepath<-file.path(script.dir,paste(names(temp.list)[i],"_aggr.csv",sep=""))
  script.filepath<-file.path(script.dir,paste(names(temp.list)[i],".sh",sep=""))
  cmd.out<-paste("#!/bin/bash\n#SBATCH --time=24:00:00  --ntasks=1 --partition=general --cpus-per-task=1 --mem=80GB --job-name=",names(temp.list)[i]," -o /home/xy48/scratch60/SARC_10X/cellranger_aggr_scripts/",names(temp.list)[i],".sh.o%J -e /home/xy48/scratch60/SARC_10X/cellranger_aggr_scripts/",names(temp.list)[i],".sh.e%J\n",sep="")
  cmd.out<-paste(cmd.out,"module load cellranger/5.0.0\nmodule load bcl2fastq2/2-20-0-foss-2018b\n",sep="")
  cmd.out<-paste(cmd.out,"cd /home/xy48/scratch60/SARC_10X/cellranger_aggr_results\n",sep="")
  cmd.out<-paste(cmd.out,"cellranger aggr --id=",names(temp.list)[i]," --csv=/home/xy48/scratch60/SARC_10X/cellranger_aggr_scripts/",names(temp.list)[i],"_aggr.csv\n",sep="")
  cat(cmd.out,file=script.filepath,append=F)
}

We moved the cellranger aggr results back to cellranger_results together with the samples with unique run.

UMI matrix extraction

We extract the nUMI vector for the samples included in the original paper.

home.dir<-"/home/yanxiting/driver_Farnam"
source(file.path(home.dir,"Rprogram/my_functions.R"))
library(xlsx)
runtable.filepath<-file.path(home.dir,"scratch60/SARC_10X/SraRunTable.txt")
id.filepath<-file.path(home.dir,"scratch60/SARC_10X/scRNA_ids.xlsx")

run.table<-read.table(runtable.filepath,sep="\t",header=T,check.names = F)
id.table<-read.xlsx(id.filepath,sheetIndex = 1,check.names=F)

my.table<-run.table[run.table$`GEO_Accession (exp)`%in%as.matrix(id.table)[,1],]

filenames<-list.files(file.path(home.dir,"scratch60/SARC_10X/cellranger_results"))

merged.data<-numeric()
for(i in 1:length(filenames)){
  if(substr(filenames[i],1,1)=="G"){
    matrix.dir<-file.path(home.dir,"scratch60/SARC_10X/cellranger_results",filenames[i],"outs","count","filtered_feature_bc_matrix")
  }else{
    matrix.dir<-file.path(home.dir,"scratch60/SARC_10X/cellranger_results",filenames[i],"outs","filtered_feature_bc_matrix")
  }
  
  barcode.path <- file.path(matrix.dir, "barcodes.tsv.gz")
  features.path <- file.path(matrix.dir, "features.tsv.gz")
  matrix.path <- file.path(matrix.dir, "matrix.mtx.gz")
  mat <- readMM(file = matrix.path)
  feature.names = read.delim(features.path,
                             header = FALSE,
                             stringsAsFactors = FALSE)
  barcode.names = read.delim(barcode.path,
                             header = FALSE,
                             stringsAsFactors = FALSE)
  if(substr(filenames[i],1,1)=="G"){
    sample.name<-filenames[i]
  }else{
    sample.name<-as.matrix(my.table)[my.table$Run==filenames[i],"Sample Name"]
  }
  
  colnames(mat) = paste0(sample.name,"_",barcode.names$V1)
  rownames(mat) = feature.names$V1
  
  if(i==1){
    merged.data<-mat
    gene.names<-rownames(mat)
  }else{
    merged.data<-cbind(merged.data,mat[gene.names,])
  }
}

output.filepath<-file.path("/home/yanxiting/driver_Farnam/scratch60/SARC_10X/merged_nUMI_45.rds"))
Error: unexpected ')' in "output.filepath<-file.path("/home/yanxiting/driver_Farnam/scratch60/SARC_10X/merged_nUMI_45.rds"))"

We transferred the cellranger results back to farnam to extract the nUMI results. We also add the samples with multiple runs into the merged matrix.

# samples with multiple runs have names of the GEO accession number.
source("/home/yanxiting/driver_Farnam/Rprogram/my_functions.R")
library(Matrix)
cellranger.dir<-"/home/yanxiting/driver_Farnam/scratch60/SARC_10X/cellranger_results"
runtable.filepath<-"/home/yanxiting/driver_Farnam/scratch60/SARC_10X/SraRunTable.txt"

# load in the run table
run.table<-read.table(runtable.filepath,sep="\t",header=T,check.names=F,stringsAsFactors = FALSE)

# load in the run names and the GSM IDs under the cell ranger results folder
temp.filenames<-list.files(cellranger.dir)

sample.rep<-temp.filenames[substr(temp.filenames,1,3)=="GSM"]
sample.uni<-temp.filenames[substr(temp.filenames,1,3)!="GSM"]
my.run.table<-run.table[as.matrix(run.table)[,"Sample Name"]%in%sample.rep | as.matrix(run.table)[,"Run"]%in%sample.uni,]
# extract the nUMI matrix
dirnames<-list.files(cellranger.dir)
dirnames<-dirnames[substr(dirnames,1,4)=="SRR1"]

for(i in 1:length(dirnames)){
  run.name<-dirnames[i]
  sample.name<-run.table[run.table[,"Run"]==run.name,"Sample Name"]
  matrix_dir<-file.path(cellranger.dir,dirnames[i],"outs","filtered_feature_bc_matrix")
  barcode.path <- file.path(matrix_dir, "barcodes.tsv.gz")
  features.path <- file.path(matrix_dir, "features.tsv.gz")
  matrix.path <- file.path(matrix_dir, "matrix.mtx.gz")
  mat <- readMM(file = matrix.path)
  feature.names = read.delim(features.path,
                             header = FALSE,
                             stringsAsFactors = FALSE)
  barcode.names = read.delim(barcode.path,
                             header = FALSE,
                             stringsAsFactors = FALSE)
  colnames(mat) = paste0(sample.name,"_",barcode.names$V1)
  rownames(mat) = feature.names$V1
  
  output.filepath<-file.path("/home/yanxiting/driver_Farnam/scratch60/SARC_10X/cellranger_results",paste0(sample.name,"_",run.name,"_nUMI.rds"))
  saveRDS(mat,file=output.filepath,refhook=NULL)
}

Since we have limited storage on the hpc, we back up the fastq.gz files and the cell ranger output folders. We extracted the nUMI matrix of each sample separately and deleted all other files to make room. All fastq.gz files and their corresponding cell ranger output folders are backed up on google drive under /Volumes/GoogleDrive/My Drive/GraceBackup/GRADS/SARC_PBMC/SARC_10X.

Seurat Analysis

We load the data into Seurat for downstream analysis, especially for the integrated analysis.

First, we create the Seurat object and preliminary filtering on genes and

# due to the unavailability of the cluster, we ran these codes on tac4 server with the same versions of R and packages
# srun --pty --x11 -p pi_kaminski -t 12:00:00 --ntasks=1 --nodes=1 --cpus-per-task=1 --mem=60152 bash
# module restore seurat
#home.dir<-"/home/yanxiting/driver_Farnam"
home.dir<-"/home/yanxiting/Documents/Research/GRADS_SARC_PBMC"
source("/home/yanxiting/Rprogram/my_functions.R")

#output.dir<-file.path(home.dir,"scratch60/SARC_10X/Seurat")
output.dir<-file.path(home.dir,"scRNA-seq/Seurat")

if(file.exists(output.dir)==F){
dir.create(output.dir)
}

# Load the merged data
merged.data<-readRDS(file.path(home.dir,"Data/merged_nUMI_45.rds"),refhook=NULL)

# load in the genes.gtf to convert ensembl ID to gene names
gtf.filepath<-"/home/yanxiting/driver_Farnam/scratch60/SARC_10X/refdata-gex-GRCh38-2020-A/genes/genes.gtf"
gtf.matrix<-read.table(gtf.filepath,sep="\t",header=F,check.names = F, stringsAsFactors = FALSE,skip = 5)
gtf.matrix<-gtf.matrix[gtf.matrix[,3]=="gene",]
temp<-gtf.matrix[,9]

gene.id<-unname(sapply(sapply(temp,my.element.extract,splitchar="; ",index=1),my.element.extract,splitchar=" ",index=-1))
gene.name<-unname(sapply(sapply(temp,my.element.extract,splitchar="; ",index=4),my.element.extract,splitchar=" ",index=-1))
gene.list<-split(gene.id,gene.name)
# get the list of ensembl IDs from mitochondrial genes
mt.genes<-unlist(gene.list[grep("^MT-",names(gene.list))])

#geoff.data <- Read10X(data.dir = file.path(data.dir,"outs","filtered_gene_bc_matrices_mex","GRCh38"))
#load(file.path(home.dir,"scratch_kaminski/public/Backup/Jonas/R_objects/10x_ChuppAsthma.mtx.hybrid_gene_symbols_wo_background_03_1119.Robj"))
gcql1 <- CreateSeuratObject(counts = merged.data, project = "SARC_10X",min.cells = 3,  min.features = 200)
#gcql1[["percent.mt"]] <- PercentageFeatureSet(gcql1, pattern = "^MT-")
gcql1[["percent.mt"]] <- PercentageFeatureSet(gcql1, features = mt.genes)

cat("\n")
cat("Originally, there are ",nrow(merged.data)," genes and ",ncol(merged.data)," cells in the data.\n",sep="")
Originally, there are 36601 genes and 115826 cells in the data.
cat("After the first step filtering, there are ",nrow(gcql1@assays$RNA@data)," genes and ",ncol(gcql1@assays$RNA@data)," cells in the filtered data.\n",sep="")
After the first step filtering, there are 23851 genes and 73145 cells in the filtered data.
rm(merged.data)
#gc(verbose=F)
invisible(gc())

# load in the phenotype data of all the samples
id.filepath<-file.path(home.dir,"Data/scRNA_ids.xlsx")
runtable.filepath<-file.path(home.dir,"Data/SraRunTable.txt")

run.table<-read.table(runtable.filepath,sep="\t",header=T,check.names = F)
id.table<-read.xlsx(id.filepath,sheetIndex = 1,check.names=F)

# get the sample names for all the cells
my.ident<-unname(sapply(colnames(gcql1@assays$RNA@counts),my.element.extract,splitchar="_",index=1))
names(my.ident)<-colnames(gcql1@assays$RNA@counts)

temp<-split(as.matrix(run.table)[,"study_classification"],as.matrix(run.table)[,"Sample Name"])
temp<-lapply(temp,unique)
my.disease<-unlist(temp[my.ident])

my.samplenames<-my.ident
disease.samplenames<-paste(my.disease,"_",my.samplenames,sep="")

# scRNA-seq QC metric.
#mito.genes1 <- grep(pattern = "^MT-", x = rownames(gcql1@assays$RNA@counts), value = TRUE)
#percent.mito1 <- Matrix::colSums(gcql1@assays$RNA@counts[mito.genes1, ])/Matrix::colSums(gcql1@assays$RNA@counts)

# AddMetaData adds columns to object@meta.data, and is a great place to
# stash QC stats
gcql1 <- AddMetaData(object = gcql1, metadata = my.disease, col.name = "disease")
gcql1 <- AddMetaData(object = gcql1, metadata = my.samplenames, col.name = "sample.names")
gcql1 <- AddMetaData(object = gcql1, metadata = my.ident, col.name = "sample")
gcql1 <- AddMetaData(object = gcql1, metadata = disease.samplenames, col.name = "disease.samplenames")
temp<-rep("pbmc",nrow(gcql1@meta.data))
gcql1 <- AddMetaData(object = gcql1, metadata = temp, col.name = "tissue")

# filter the data to only keep genes present (>0 in >=1% of all the cells) in >=3 subjects
gene.pres<-numeric()
sample.names.unique<-unique(gcql1@meta.data$sample.names)

for(i in 1:length(sample.names.unique)){
  temp.matrix<-gcql1@assays$RNA@counts[,gcql1@meta.data$sample.names==sample.names.unique[i]]
  temp.vect<-apply(temp.matrix>0,1,sum)
  gene.pres<-cbind(gene.pres,temp.vect>=(ncol(temp.matrix)*0.01))
}

gene.names<-rownames(gcql1@assays$RNA@counts)[apply(gene.pres,1,sum)>=3]
gcql1.orig<-gcql1
gcql1<-subset(gcql1.orig,features=gene.names)

cat("After the first and second steps of filtering, there are ",nrow(gcql1@assays$RNA@counts)," genes and ",ncol(gcql1@assays$RNA@counts)," cells in the data.\n",sep="")
After the first and second steps of filtering, there are 12834 genes and 73145 cells in the data.

Second, we had a third filtering step on cells based on the nGene and percent.mito. Here are the plots to help us decide the threshold of the filtering.


#g1<-VlnPlot(object = gcql1, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3,group.by="samplenames",combine=T)

g2<-VlnPlot(object = gcql1, features= c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3,group.by = "disease.samplenames",combine=T)
grid.arrange(g2,ncol=1)

# only keep cells with nFeature_RNA>=330 or <=6000
#gcql1<-subset(gcql1,subset= nFeature_RNA >= 300 & nFeature_RNA<= 6000 & percent.mt<= 50)
gcql1<-subset(gcql1,subset= nFeature_RNA >= 300 & percent.mt<= 50)
cat("After the third filtering step, we have ",nrow(gcql1@assays$RNA@counts)," genes and ",ncol(gcql1@assays$RNA@counts)," cells.\n",sep="")
After the third filtering step, we have 12834 genes and 70244 cells.

Based on the quality control plots, in the third filtering step, we removed cells with >50% mitochondrial percentage or <300 expressed genes. After this second filtering step, we have 12834 genes and 70244 cells remained.

Third, we normalize the unimputed and save the SAVER imputed data as the normalized data. These data are saved for downstream further processing.

# normalize the data and save the results
gcql1 <- NormalizeData(gcql1, normalization.method = "LogNormalize",  scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
saveRDS(gcql1, file = file.path(output.dir,"1_normalized_data_unimputed_1.rds"))

#gcql1.saver<-NormalizeData(object = gcql1.saver, normalization.method = "LogNormalize",  scale.factor = 10000)
#saveRDS(gcql1.saver, file = file.path(output.dir,"preprocessed_data_saverimputed_1.rds"))

cat("the preprocessed data for the unimputed data was saved as ",file.path(output.dir,"1_normalized_data_unimputed_1.rds"),"\n",sep="")
the preprocessed data for the unimputed data was saved as /home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA-seq/Seurat/1_normalized_data_unimputed_1.rds
cat("After the normalization, there are ",nrow(gcql1@assays$RNA@data)," genes and ",ncol(gcql1@assays$RNA@data)," cells\n",sep="")
After the normalization, there are 12834 genes and 70244 cells
#cat("the preprocessed data for the SAVER imputed data was saved as ",file.path(output.dir,"preprocessed_data_saverimputed_1.rds"),"\n",sep="")

Original Cell Clustering

First, we find variable genes.

features.n<-2000

We load in the normalized unimputed data and find the top 2000 variable genes for cell clustering. The normalized data was further scaled to remove the effect of nUMI and percent.mito for further cell clustering purpose.

Then we scale the data to remove the effect from nUMI and percent.mito. Here we decide to scale for all genes so that later on, the heatmap will be generated using the scaled data.

#maybe regress cell cycle http://satijalab.org/seurat/cell_cycle_vignette.html
all.genes<-rownames(gcql1)
#gcql1 <- ScaleData(gcql1,features = all.genes, vars.to.regress = c("nCount_RNA", "percent.mt"))
gcql1 <- ScaleData(gcql1)
#output.filepath<-file.path(output.dir,"2_scaled_allgenes_unimputed.rds")
#saveRDS(gcql1, file = output.filepath)

We perform the principal component analysis on the variable genes to decide the number of PCs to use for clustering.

gcql1 <- RunPCA(object = gcql1, features = VariableFeatures(gcql1), npcs=200,verbose=F)

To see if there are PCs that are subject specific, the 2D PCA plots using the top 3 PCs are as follows for the un-imputed data.

#par(mfrow=c(2,2))
fig.1<-DimPlot(object = gcql1, dims = c(1,2), pt.size=0.5)
fig.2<-DimPlot(object = gcql1, dims = c(2,3), pt.size=0.5)
fig.3<-DimPlot(object = gcql1, dims = c(1,3), pt.size=0.5)
grid.arrange(fig.1,fig.2,fig.3,ncol=1)

We draw the heatmap of the top genes for each PC.

DimHeatmap(gcql1, dims = 1:10, cells = 500, balanced = TRUE)

DimHeatmap(gcql1, dims = 11:20, cells = 500, balanced = TRUE)

DimHeatmap(gcql1, dims = 21:30, cells = 500, balanced = TRUE)

DimHeatmap(gcql1, dims = 31:40, cells = 500, balanced = TRUE)

DimHeatmap(gcql1, dims = 41:50, cells = 500, balanced = TRUE)

DimHeatmap(gcql1, dims = 51:60, cells = 500, balanced = TRUE)

DimHeatmap(gcql1, dims = 61:70, cells = 500, balanced = TRUE)
DimHeatmap(gcql1, dims = 71:80, cells = 500, balanced = TRUE)

We use the following Jackstraw plots and the Elbowplot to decide the number of PCs to use for downstream analysis.

gcql1 <- JackStraw(object = gcql1, num.replicate = 100, verbose = TRUE,dims=200)

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~02d 05h 12m 27s
  |+                                                 | 2 % ~02d 05h 54m 46s
  |++                                                | 3 % ~02d 05h 33m 56s
  |++                                                | 4 % ~02d 06h 06m 09s
  |+++                                               | 5 % ~02d 06h 15m 49s
  |+++                                               | 6 % ~02d 05h 50m 36s
  |++++                                              | 7 % ~02d 06h 46m 44s
  |++++                                              | 8 % ~02d 05h 59m 25s
  |+++++                                             | 9 % ~02d 05h 03m 28s
  |+++++                                             | 10% ~02d 04h 46m 39s
  |++++++                                            | 11% ~02d 04h 04m 22s
  |++++++                                            | 12% ~02d 03h 19m 08s
  |+++++++                                           | 13% ~02d 02h 59m 59s
  |+++++++                                           | 14% ~02d 02h 18m 28s
  |++++++++                                          | 15% ~02d 02h 02m 28s
  |++++++++                                          | 16% ~02d 01h 30m 52s
  |+++++++++                                         | 17% ~02d 00h 38m 13s
  |+++++++++                                         | 18% ~02d 00h 03m 54s
  |++++++++++                                        | 19% ~01d 23h 20m 43s
  |++++++++++                                        | 20% ~01d 22h 25m 39s
  |+++++++++++                                       | 21% ~01d 21h 48m 59s
  |+++++++++++                                       | 22% ~01d 21h 11m 54s
  |++++++++++++                                      | 23% ~01d 20h 41m 51s
  |++++++++++++                                      | 24% ~01d 20h 04m 16s
  |+++++++++++++                                     | 25% ~01d 19h 24m 53s
  |+++++++++++++                                     | 26% ~01d 18h 44m 19s
  |++++++++++++++                                    | 27% ~01d 18h 09m 19s
  |++++++++++++++                                    | 28% ~01d 17h 40m 44s
  |+++++++++++++++                                   | 29% ~01d 17h 15m 57s
  |+++++++++++++++                                   | 30% ~01d 16h 45m 35s
  |++++++++++++++++                                  | 31% ~01d 16h 04m 13s
  |++++++++++++++++                                  | 32% ~01d 15h 25m 03s
  |+++++++++++++++++                                 | 33% ~01d 14h 53m 22s
  |+++++++++++++++++                                 | 34% ~01d 14h 18m 09s
  |++++++++++++++++++                                | 35% ~01d 13h 43m 30s
  |++++++++++++++++++                                | 36% ~01d 13h 04m 32s
  |+++++++++++++++++++                               | 37% ~01d 12h 25m 20s
  |+++++++++++++++++++                               | 38% ~01d 11h 59m 17s
  |++++++++++++++++++++                              | 39% ~01d 11h 24m 27s
  |++++++++++++++++++++                              | 40% ~01d 10h 45m 58s
  |+++++++++++++++++++++                             | 41% ~01d 10h 09m 04s
  |+++++++++++++++++++++                             | 42% ~01d 09h 36m 01s
  |++++++++++++++++++++++                            | 43% ~01d 08h 59m 30s
  |++++++++++++++++++++++                            | 44% ~01d 08h 26m 53s
  |+++++++++++++++++++++++                           | 45% ~01d 07h 51m 58s
  |+++++++++++++++++++++++                           | 46% ~01d 07h 13m 28s
  |++++++++++++++++++++++++                          | 47% ~01d 06h 35m 32s
  |++++++++++++++++++++++++                          | 48% ~01d 06h 00m 43s
  |+++++++++++++++++++++++++                         | 49% ~01d 05h 25m 01s
  |+++++++++++++++++++++++++                         | 50% ~01d 04h 54m 35s
  |++++++++++++++++++++++++++                        | 51% ~01d 04h 20m 22s
  |++++++++++++++++++++++++++                        | 52% ~01d 03h 44m 46s
  |+++++++++++++++++++++++++++                       | 53% ~01d 03h 06m 54s
  |+++++++++++++++++++++++++++                       | 54% ~01d 02h 32m 34s
  |++++++++++++++++++++++++++++                      | 55% ~01d 01h 58m 00s
  |++++++++++++++++++++++++++++                      | 56% ~01d 01h 22m 57s
  |+++++++++++++++++++++++++++++                     | 57% ~01d 00h 47m 49s
  |+++++++++++++++++++++++++++++                     | 58% ~01d 00h 11m 25s
  |++++++++++++++++++++++++++++++                    | 59% ~23h 35m 24s  
  |++++++++++++++++++++++++++++++                    | 60% ~23h 01m 55s  
  |+++++++++++++++++++++++++++++++                   | 61% ~22h 28m 22s  
  |+++++++++++++++++++++++++++++++                   | 62% ~21h 54m 21s  
  |++++++++++++++++++++++++++++++++                  | 63% ~21h 20m 02s  
  |++++++++++++++++++++++++++++++++                  | 64% ~20h 45m 15s  
  |+++++++++++++++++++++++++++++++++                 | 65% ~20h 09m 23s  
  |+++++++++++++++++++++++++++++++++                 | 66% ~19h 35m 07s  
  |++++++++++++++++++++++++++++++++++                | 67% ~19h 00m 51s  
  |++++++++++++++++++++++++++++++++++                | 68% ~18h 26m 05s  
  |+++++++++++++++++++++++++++++++++++               | 69% ~17h 50m 29s  
  |+++++++++++++++++++++++++++++++++++               | 70% ~17h 15m 01s  
  |++++++++++++++++++++++++++++++++++++              | 71% ~16h 40m 46s  
  |++++++++++++++++++++++++++++++++++++              | 72% ~16h 05m 28s  
  |+++++++++++++++++++++++++++++++++++++             | 73% ~15h 31m 50s  
  |+++++++++++++++++++++++++++++++++++++             | 74% ~14h 57m 32s  
  |++++++++++++++++++++++++++++++++++++++            | 75% ~14h 23m 28s  
  |++++++++++++++++++++++++++++++++++++++            | 76% ~13h 48m 32s  
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~13h 14m 35s  
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~12h 40m 43s  
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~12h 05m 56s  
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~11h 31m 24s  
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~10h 56m 50s  
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~10h 21m 26s  
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~09h 47m 24s  
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~09h 12m 26s  
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~08h 38m 07s  
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~08h 02m 53s  
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~07h 28m 14s  
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~06h 53m 27s  
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~06h 18m 30s  
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~05h 43m 52s  
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~05h 09m 38s  
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~04h 35m 19s  
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~04h 00m 59s  
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~03h 26m 30s  
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~02h 51m 58s  
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~02h 17m 24s  
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~01h 43m 07s  
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~01h 08m 43s  
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~34m 21s      
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=02d 09h 14m 14s

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~06s          
  |+                                                 | 2 % ~05s          
  |++                                                | 3 % ~05s          
  |++                                                | 4 % ~05s          
  |+++                                               | 5 % ~05s          
  |+++                                               | 6 % ~05s          
  |++++                                              | 7 % ~05s          
  |++++                                              | 8 % ~05s          
  |+++++                                             | 9 % ~05s          
  |+++++                                             | 10% ~05s          
  |++++++                                            | 11% ~05s          
  |++++++                                            | 12% ~05s          
  |+++++++                                           | 13% ~05s          
  |+++++++                                           | 14% ~05s          
  |++++++++                                          | 15% ~05s          
  |++++++++                                          | 16% ~04s          
  |+++++++++                                         | 17% ~04s          
  |+++++++++                                         | 18% ~04s          
  |++++++++++                                        | 19% ~04s          
  |++++++++++                                        | 20% ~04s          
  |+++++++++++                                       | 21% ~04s          
  |+++++++++++                                       | 22% ~04s          
  |++++++++++++                                      | 23% ~04s          
  |++++++++++++                                      | 24% ~04s          
  |+++++++++++++                                     | 25% ~04s          
  |+++++++++++++                                     | 26% ~04s          
  |++++++++++++++                                    | 27% ~04s          
  |++++++++++++++                                    | 28% ~04s          
  |+++++++++++++++                                   | 29% ~04s          
  |+++++++++++++++                                   | 30% ~04s          
  |++++++++++++++++                                  | 31% ~04s          
  |++++++++++++++++                                  | 32% ~04s          
  |+++++++++++++++++                                 | 33% ~04s          
  |+++++++++++++++++                                 | 34% ~04s          
  |++++++++++++++++++                                | 35% ~04s          
  |++++++++++++++++++                                | 36% ~04s          
  |+++++++++++++++++++                               | 37% ~03s          
  |+++++++++++++++++++                               | 38% ~03s          
  |++++++++++++++++++++                              | 39% ~03s          
  |++++++++++++++++++++                              | 40% ~03s          
  |+++++++++++++++++++++                             | 41% ~03s          
  |+++++++++++++++++++++                             | 42% ~03s          
  |++++++++++++++++++++++                            | 43% ~03s          
  |++++++++++++++++++++++                            | 44% ~03s          
  |+++++++++++++++++++++++                           | 45% ~03s          
  |+++++++++++++++++++++++                           | 46% ~03s          
  |++++++++++++++++++++++++                          | 47% ~03s          
  |++++++++++++++++++++++++                          | 48% ~03s          
  |+++++++++++++++++++++++++                         | 49% ~03s          
  |+++++++++++++++++++++++++                         | 50% ~03s          
  |++++++++++++++++++++++++++                        | 51% ~03s          
  |++++++++++++++++++++++++++                        | 52% ~03s          
  |+++++++++++++++++++++++++++                       | 53% ~03s          
  |+++++++++++++++++++++++++++                       | 54% ~03s          
  |++++++++++++++++++++++++++++                      | 55% ~02s          
  |++++++++++++++++++++++++++++                      | 56% ~02s          
  |+++++++++++++++++++++++++++++                     | 57% ~02s          
  |+++++++++++++++++++++++++++++                     | 58% ~02s          
  |++++++++++++++++++++++++++++++                    | 59% ~02s          
  |++++++++++++++++++++++++++++++                    | 60% ~02s          
  |+++++++++++++++++++++++++++++++                   | 61% ~02s          
  |+++++++++++++++++++++++++++++++                   | 62% ~02s          
  |++++++++++++++++++++++++++++++++                  | 63% ~02s          
  |++++++++++++++++++++++++++++++++                  | 64% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 65% ~02s          
  |+++++++++++++++++++++++++++++++++                 | 66% ~02s          
  |++++++++++++++++++++++++++++++++++                | 67% ~02s          
  |++++++++++++++++++++++++++++++++++                | 68% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~02s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 72% ~02s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~01s          
  |+++++++++++++++++++++++++++++++++++++             | 74% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~01s          
  |++++++++++++++++++++++++++++++++++++++            | 76% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~01s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~01s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=05s  
gcql1 <- ScoreJackStraw(gcql1, dims = 1:200)
JackStrawPlot(gcql1, dims = 1:100)

ElbowPlot(object = gcql1,ndims=100)

pc.num<-35

Based on the JackStraw plot and the Elbow plot, we decided to use the top 35 PCs for the unimputed data.

saveRDS(gcql1, file = file.path(output.dir,paste("2_dimreduction_data_",length(gcql1@assays$RNA@var.features),"vargenes_pipeline1.rds",sep="")))
# we save the file as preprocessed_data_saver.rds to prevent any mistakes but we change the file name back to preprocessed_data.rds before performing downstream analysis.

cat("We save the R project as\n")
We save the R project as
cat("unimputed data:\t",file.path(output.dir,paste("2_dimreduction_data_",length(gcql1@assays$RNA@var.features),"vargenes_pipeline1.rds",sep="")),"\n",sep="")
unimputed data: /home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA-seq/Seurat/2_dimreduction_data_2000vargenes_pipeline1.rds

Based on the Jackstraw plots, we decided to use the 35 top PCs for clustering.

Data visualization

UMAP

To identify potential outlying cells and samples, we first label the tSNE plot using disease_samplenames.

sample1<-FindNeighbors(gcql1,dims=1:pc.num)
Computing nearest neighbor graph
Computing SNN
sample1<-FindClusters(sample1,resolution=1.2)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 70244
Number of edges: 2352218

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8723
Number of communities: 43
Elapsed time: 24 seconds
1 singletons identified. 42 final clusters.
sample1<-RunUMAP(sample1,dims=1:pc.num)
Warning: The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session
10:23:09 UMAP embedding parameters a = 0.9922 b = 1.112
10:23:09 Read 70244 rows and found 35 numeric columns
10:23:09 Using Annoy for neighbor search, n_neighbors = 30
10:23:09 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:23:32 Writing NN index file to temp file /tmp/RtmpqMZeUs/filed37f3287ef
10:23:32 Searching Annoy index using 1 thread, search_k = 3000
10:24:06 Annoy recall = 100%
10:24:08 Commencing smooth kNN distance calibration using 1 thread
10:24:14 Initializing from normalized Laplacian + noise
10:24:21 Commencing optimization for 200 epochs, with 3226940 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:25:55 Optimization finished
fig1<-DimPlot(sample1,reduction="umap")
fig2<-DimPlot(sample1,reduction="umap",group.by="disease.samplenames")
fig3<-DimPlot(sample1,reduction="umap",group.by="disease")
grid.arrange(fig1,fig2,fig3,ncol=1)

tSNE plot

sample1 <- RunTSNE(sample1, dims= 1:pc.num)
Error in Rtsne.default(X = object, dims = dim.embed, ...) : 
  Remove duplicates before running TSNE.

data saving

saveRDS(sample1, file = file.path(output.dir,paste("2_visualization_data_",length(gcql1@assays$RNA@var.features),"vargenes_pipeline1.rds",sep="")))
cat("We saved the seurat object with UMAP and tSNE as ",file.path(output.dir,paste("2_visualization_data_",length(sample1@assays$RNA@var.features),"vargenes_pipeline1.rds",sep="")),"\n",sep="")
We saved the seurat object with UMAP and tSNE as /home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA-seq/Seurat/2_visualization_data_2000vargenes_pipeline1.rds

Integrative Analysis

my.distinct.colors<-c('#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000')

To describe the cell types captured in the data, the integrated analysis may provide cleaner results. Therefore, we conduct the integrated analysis of all the asthma 10X data using Seurat V3.1 in this note to reduce the subject effect in the data visulization and the trajectory analysis at least.

data loading

########################################################
# 1. load in the singler object
data.dir<-output.dir
sample1<-readRDS(file.path(data.dir,"2_visualization_data_2000vargenes_pipeline1.rds"),refhook = NULL)

sample1.raw<-sample1

integrating data

Split the whole dataset based on subject ID (fresh and DSMO samples from the same subject are considered as one sample). Find anchors to integrate all the datasets together.

sample1.combined<-IntegrateData(anchorset=sample1.anchors,dims=1:40,verbose=FALSE)
Warning: Adding a command log without an assay associated with it
output.filepath<-file.path(output.dir,"integrated_analysis","seurat_object_integrated_allcells.rds")
saveRDS(sample1.combined,file=output.filepath)
cat("We saved the integrated data for all cells as ",output.filepath,"\n",sep="")
We saved the integrated data for all cells as /home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA-seq/Seurat/integrated_analysis/seurat_object_integrated_allcells.rds
output.filepath<-file.path(output.dir,"integrated_analysis","seurat_object_original_allcells.rds")
saveRDS(sample1,file=output.filepath)
cat("We saved the original data for all cells as ",output.filepath,"\n",sep="")
We saved the original data for all cells as /home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA-seq/Seurat/integrated_analysis/seurat_object_original_allcells.rds

Cluster the cells using the integrated data.

DefaultAssay(sample1.combined) <- "integrated"
# Run the standard workflow for visualization and clustering
sample1.combined <- ScaleData(sample1.combined, verbose = FALSE)
sample1.combined <- RunPCA(sample1.combined, npcs = 40, verbose = FALSE)
# t-SNE and Clustering
sample1.combined <- RunUMAP(sample1.combined, reduction = "pca", dims = 1:40)
22:37:42 UMAP embedding parameters a = 0.9922 b = 1.112
22:37:43 Read 69994 rows and found 40 numeric columns
22:37:43 Using Annoy for neighbor search, n_neighbors = 30
22:37:43 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
22:38:14 Writing NN index file to temp file /tmp/RtmpqMZeUs/filed37f111cb3
22:38:14 Searching Annoy index using 1 thread, search_k = 3000
22:38:53 Annoy recall = 100%
22:38:56 Commencing smooth kNN distance calibration using 1 thread
22:39:02 Initializing from normalized Laplacian + noise
22:39:15 Commencing optimization for 200 epochs, with 3669140 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
22:41:00 Optimization finished
sample1.combined <- FindNeighbors(sample1.combined, reduction = "pca", dims = 1:40)
Computing nearest neighbor graph
Computing SNN
sample1.combined <- FindClusters(sample1.combined, resolution = 1.2)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 69994
Number of edges: 4093949

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8816
Number of communities: 47
Elapsed time: 34 seconds
2 singletons identified. 45 final clusters.

We save the result in a rds file.

output.subdir<-file.path(output.dir,"integrated_analysis")
if(file.exists(output.subdir)==F){
  dir.create(output.subdir)
}

output.filepath<-file.path(output.subdir,"seurat_object_integrated_allcells_cellclustering_noSingleR.rds")
saveRDS(sample1.combined,file=output.filepath)
cat("We saved the seurat object of the integrated data with cell clustering results only as ",output.filepath,"\n",sep="")
We saved the seurat object of the integrated data with cell clustering results only as /home/yanxiting/Documents/Research/GRADS_SARC_PBMC/scRNA-seq/Seurat/integrated_analysis/seurat_object_integrated_allcells_cellclustering_noSingleR.rds

Visualizing integrated data

library(cowplot)
p1 <- DimPlot(sample1.combined, reduction = "umap", group.by = "sample.names")
p2 <- DimPlot(sample1.combined, reduction = "umap", label = TRUE)
#p3<-DimPlot(sample1.combined, reduction = "umap", group.by="singler.hpca.cluster.merged", label = TRUE,cols=my.distinct.colors)
#p4<-DimPlot(sample1.combined, reduction = "umap", group.by="singler.hpca.cluster", label = TRUE,cols=my.distinct.colors)
p5<-DimPlot(sample1.combined, reduction = "umap", group.by="disease", label = TRUE,cols=my.distinct.colors)
#plot_grid(p2, p1, p3,p4,p5,ncol=1)
plot_grid(p2, p1,p5,ncol=1)

Integrate with existing data

We obtained another PBMC 10X data annotated in Kaminski lab so that we can integrate this dataset with the annotated data to annotate the cells in this dataset.

Visualize the integrated data by splitting it to different clusters.

fig.list<-list()
celltype.names<-as.numeric(unique(as.character(Idents(sample1.combined))))
celltype.names<-sort(celltype.names,decreasing=F)
for(i in 1:length(celltype.names)){
  temp.map<-as.character(Idents(sample1.combined))
  temp.map[temp.map!=celltype.names[i]]="other"
  sample1.combined@meta.data$temp.map<-temp.map
  fig.list[[2*(i-1)+1]]<-DimPlot(sample1.combined, reduction = "umap", label = FALSE,group.by="ident")
  #fig.list[[2*i]]<-DimPlot(sample1.combined, reduction = "umap", label = FALSE,group.by="temp.map",cols=c(my.distinct.colors[i],"grey"))
  fig.list[[2*i]]<-DimPlot(sample1.combined, reduction = "umap", label = FALSE,group.by="temp.map",cols=c("red","grey"))
}

do.call(grid.arrange,c(fig.list,ncol=2))

Visualize the integrated data by splitting it to different subjects and different cell type annotation (old).

fig.list<-list()
celltype.names<-unique(as.character(sample1.combined@meta.data$singler.hpca.cluster))
celltype.names<-sort(celltype.names,decreasing=T)
for(i in 1:length(celltype.names)){
  temp.map<-as.character(sample1.combined@meta.data$singler.hpca.cluster)
  temp.map[temp.map!=celltype.names[i]]<-"zother"
  sample1.combined@meta.data$temp.map<-temp.map
  fig.list[[i]]<-DimPlot(sample1.combined, reduction = "umap",group.by="temp.map",label = FALSE,pt.size=0.5,cols=c(my.distinct.colors[i],"grey"))
}
do.call(grid.arrange,c(fig.list,ncol=2))
LS0tCnRpdGxlOiAiR1NFQSBhbmFseXNpcyBvZiBTQVJDIGFzc29jaWF0ZWQgZ2VuZXMiCmF1dGhvcjogIlhpdGluZyBZYW4iCmRhdGU6ICIxMC8wMi8yMDE5IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZmlnX2NhcHRpb246IHllcwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNgogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9VFJVRSxlY2hvID0gVFJVRSxjYWNoZT1UUlVFLHdhcm5pbmc9RkFMU0UsbWVzc2FnZSA9IEZBTFNFLHJlc3VsdHM9J2hvbGQnLGNhY2hlLmxhenkgPSBGQUxTRSkKa25pdHI6Om9wdHNfa25pdCRzZXQoZXZhbC5hZnRlciA9ICdmaWcuY2FwJyxkZXY9YygncG5nJywncG9zdHNjcmlwdCcpKQoKCmxpYnJhcnkoY2FwdGlvbmVyKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShkcGx5cikKbGlicmFyeShFbnNEYi5Ic2FwaWVucy52ODYpCmxpYnJhcnkoZ2RhdGEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShncGxvdHMpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ3JpZEdyYXBoaWNzKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNpbmdsZVIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHZpcmlkaXNMaXRlKQpsaWJyYXJ5KHhsc3gpCmxpYnJhcnkocmFuZG9tY29sb1IpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZGF0YSkKbGlicmFyeShrbml0cikKbGlicmFyeShjYXB0aW9uZXIpCmxpYnJhcnkobmxtZSkKbGlicmFyeShyZ2wpCmxpYnJhcnkoZ3Bsb3RzKQpsaWJyYXJ5KFdHQ05BKQpsaWJyYXJ5KHhsc3gpCmxpYnJhcnkocmFuZG9tY29sb1IpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCgprbml0X2hvb2tzJHNldCh3ZWJnbCA9IGhvb2tfd2ViZ2wpCgoKdGFibGVfbnVtc18xIDwtIGNhcHRpb25lcjo6Y2FwdGlvbmVyKHByZWZpeD0iVGFibGUiLGxldmVscz0xKQpmaWd1cmVfbnVtc18xPC0gY2FwdGlvbmVyOjpjYXB0aW9uZXIocHJlZml4PSJGaWd1cmUiLGxldmVscz0xKQoKCnRhYmxlX251bXNfMiA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IlRhYmxlIixsZXZlbHM9MikKZmlndXJlX251bXNfMjwtIGNhcHRpb25lcjo6Y2FwdGlvbmVyKHByZWZpeD0iRmlndXJlIixsZXZlbHM9MikKCnRhYmxlX251bXNfMyA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IlRhYmxlIixsZXZlbHM9MykKZmlndXJlX251bXNfMyA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IkZpZ3VyZSIsbGV2ZWxzPTMpCgpob21lLmRpcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2UiCiNob21lLmRpcjwtIi9ob21lL3h5NDgiCiNzb3VyY2UocGFzdGUoaG9tZS5kaXIsIi9ScHJvZ3JhbS9teV9mdW5jdGlvbnMuUiIsc2VwPSIiKSkKYGBgCiMgRGF0YSBEb3dubG9hZGluZwoKIyMgc3JhdG9vbHMKV2UgZG93bmxvYWRlZCB0aGUgZGF0YSB1c2luZyBTUkF0b29scyBvbiBncmFjZSB1c2luZyB0aGUgc2NyaXB0cyBnZW5lcmF0ZWQgYnkgdGhlIGZvbGxvd2luZyBjb2Rlcy4gV2UgZG93bmxvYWRlZCB0aGUgc3JhdG9vbHMgZnJvbSBnaXRodWIgYW5kIGluc3RhbGxlZCBpdCBvbiBncmFjZSB3aXRoIHNldHVwLiBBbGwgZmlsZXMgZG93bmxvYWRlZCB3ZXJlIG5vdCB6aXBwZWQgc28gd2UgYWxzbyB6aXBwZWQgZWFjaCBmaWxlIGludG8gZmFzdHEuZ3ogZmlsZS4KCmBgYHtyfQppZC5saXN0PC1yZWFkTGluZXMoIi9Vc2Vycy95YW54aXRpbmcvRG93bmxvYWRzL1NSUl9BY2NfTGlzdCgyKS50eHQiKQp0ZW1wMTwtcGFzdGUoIi9ob21lL3h5NDgvc2NyYXRjaF9wYWxtZXIvc3JhdG9vbGtpdC9zcmF0b29sa2l0LjMuMC4wLXVidW50dTY0L2Jpbi9wcmVmZXRjaC4zLjAuMCAtWCA5OTk5OTk5OTk5OTk5ICIsaWQubGlzdCwiXG4iLHNlcD0iIikKdGVtcDI8LXBhc3RlKCIvaG9tZS94eTQ4L3NjcmF0Y2hfcGFsbWVyL3NyYXRvb2xraXQvc3JhdG9vbGtpdC4zLjAuMC11YnVudHU2NC9iaW4vZmFzdHEtZHVtcC4zLjAuMCAtLXNwbGl0LWZpbGVzIC4vIixpZC5saXN0LCIuc3JhXG4iLHNlcD0iIikKdGVtcDM8LXJlcCgiY2QgL2hvbWUveHk0OC9zY3JhdGNoX3BhbG1lci9TQVJDXzEweC90ZXN0L3NyYVxuIixsZW5ndGgodGVtcDEpKQp0ZW1wNDwtcGFzdGUoImd6aXAgLi8iLGlkLmxpc3QsIl8qLmZhc3RxXG4iLHNlcD0iIikKdGVtcDU8LXJlcCgic3NoIHRyYW5zZmVyXG4iLGxlbmd0aCh0ZW1wMSkpCmNtZC5vdXQ8LWNiaW5kKHRlbXA1LHRlbXAxLHRlbXAzLHRlbXAyLHRlbXA0KQp0ZW1wLmNtZDwtYXBwbHkoY21kLm91dCwxLHBhc3RlLGNvbGxhcHNlPSIiLHNlcD0iIikKY2F0KHRlbXAuY21kLGZpbGU9Ii4vZG93bmxvYWRfY29tbWFuZHMudHh0IixhcHBlbmQ9RixzZXA9IiIpCgpgYGAKCiMjIGNoYW5naW5nIG5hbWVzClRoZSBjZWxsIHJhbmdlciBwaXBlbGluZSByZWNvZ25pemUgZmFzdHEgZmlsZSBuYW1lcyBhcmUgZm9ybWF0ZWQgYXMgW1NhbXBsZSBOYW1lXV9TMV9MMDBbTGFuZSBOdW1iZXJdX1tSZWFkIFR5cGVdXzAwMS5mYXN0cS5nei4gVGhlcmUgYXJlIDMgZmlsZXMgcGVyIHNhbXBsZSByZXByZXNlbnRpbmcgSTEsIFIxIGFuZCBSMiBkb3dubG9hZGVkIGJ5IHNyYXRvb2xzLCB3aGljaCB3ZXJlIG5hbWVkIGFzICpfMSwgKl8yIGFuZCAqXzMuZmFzdHEgZmlsZXMuIFRvIGNvcnJlY3RseSBydW4gY2VsbCByYW5nZXIgb24gdGhlc2UgZmlsZXMsIHdlIG5lZWQgdG8gY2hhbmdlIHRoZSBmYXN0cS5neiBuYW1lcy4KCmBgYHtyfQojIHRoaXMgd2FzIHJ1biBvbiBncmFjZS4Kc291cmNlKCJ+L1Jwcm9ncmFtL215X2Z1bmN0aW9ucy5SIikKZGF0YS5kaXI8LSIvaG9tZS94eTQ4L3NjcmF0Y2hfcGFsbWVyL1NBUkNfMTB4L3Rlc3Qvc3JhIgpmaWxlbmFtZXM8LWxpc3QuZmlsZXMoZGF0YS5kaXIpCnRlbXA8LXNhcHBseShmaWxlbmFtZXMsbXkuZWxlbWVudC5leHRyYWN0LHNwbGl0Y2hhcj0iXFwuIixpbmRleD0zKQpmaWxlbmFtZXM8LWZpbGVuYW1lc1t0ZW1wPT0iZ3oiXQpmaWxlbmFtZXM8LWZpbGVuYW1lc1shaXMubmEoZmlsZW5hbWVzKV0KCmZvcihpIGluIDE6bGVuZ3RoKGZpbGVuYW1lcykpewogIAogIGlmKHN1YnN0cihmaWxlbmFtZXNbaV0sMSw0KT09IlNSUjkiKXsKICAJZnJvbS5maWxlbmFtZTwtZmlsZW5hbWVzW2ldCiAgCXRlbXA8LW15LmVsZW1lbnQuZXh0cmFjdChmaWxlbmFtZXNbaV0sc3BsaXRjaGFyPSJcXC4iLGluZGV4PTEpCiAgCXRlbXA8LW15LmVsZW1lbnQuZXh0cmFjdCh0ZW1wLHNwbGl0Y2hhcj0iXyIsaW5kZXg9MikKICAJaWYodGVtcD09IjEiKXsKICAJCXRvLmZpbGVuYW1lPC1wYXN0ZTAobXkuZWxlbWVudC5leHRyYWN0KGZpbGVuYW1lc1tpXSxzcGxpdGNoYXI9Il8iLGluZGV4PTEpLCJfUzFfTDAwMV9JMV8wMDEuZmFzdHEuZ3oiKQogIAl9CiAgCWlmKHRlbXA9PSIyIil7CiAgCQl0by5maWxlbmFtZTwtcGFzdGUwKG15LmVsZW1lbnQuZXh0cmFjdChmaWxlbmFtZXNbaV0sc3BsaXRjaGFyPSJfIixpbmRleD0xKSwiX1MxX0wwMDFfUjFfMDAxLmZhc3RxLmd6IikKICAJfQogIAlpZih0ZW1wPT0iMyIpewogIAkJdG8uZmlsZW5hbWU8LXBhc3RlMChteS5lbGVtZW50LmV4dHJhY3QoZmlsZW5hbWVzW2ldLHNwbGl0Y2hhcj0iXyIsaW5kZXg9MSksIl9TMV9MMDAxX1IyXzAwMS5mYXN0cS5neiIpCiAgCX0KICAKICAJZmlsZS5yZW5hbWUoZnJvbT1maWxlLnBhdGgoZGF0YS5kaXIsZnJvbS5maWxlbmFtZSksdG89ZmlsZS5wYXRoKGRhdGEuZGlyLHRvLmZpbGVuYW1lKSkKICAJY2F0KCJmcm9tPSIsZmlsZS5wYXRoKGRhdGEuZGlyLGZyb20uZmlsZW5hbWUpLCJcbiIsInRvPSIsZmlsZS5wYXRoKGRhdGEuZGlyLHRvLmZpbGVuYW1lKSwiXG4iLHNlcD0iIikKICB9ZWxzZXsKICAgIAogIAlmcm9tLmZpbGVuYW1lPC1maWxlbmFtZXNbaV0KICAJdGVtcDwtbXkuZWxlbWVudC5leHRyYWN0KGZpbGVuYW1lc1tpXSxzcGxpdGNoYXI9IlxcLiIsaW5kZXg9MSkKICAJdGVtcDwtbXkuZWxlbWVudC5leHRyYWN0KHRlbXAsc3BsaXRjaGFyPSJfIixpbmRleD0yKQogIAlpZih0ZW1wPT0iMSIpewogIAkJdG8uZmlsZW5hbWU8LXBhc3RlMChteS5lbGVtZW50LmV4dHJhY3QoZmlsZW5hbWVzW2ldLHNwbGl0Y2hhcj0iXyIsaW5kZXg9MSksIl9TMV9MMDAxX1IxXzAwMS5mYXN0cS5neiIpCiAgCX0KICAJaWYodGVtcD09IjIiKXsKICAJCXRvLmZpbGVuYW1lPC1wYXN0ZTAobXkuZWxlbWVudC5leHRyYWN0KGZpbGVuYW1lc1tpXSxzcGxpdGNoYXI9Il8iLGluZGV4PTEpLCJfUzFfTDAwMV9SMl8wMDEuZmFzdHEuZ3oiKQogIAl9CiAgCWlmKHRlbXA9PSIzIil7CiAgCQl0by5maWxlbmFtZTwtcGFzdGUwKG15LmVsZW1lbnQuZXh0cmFjdChmaWxlbmFtZXNbaV0sc3BsaXRjaGFyPSJfIixpbmRleD0xKSwiX1MxX0wwMDFfSTFfMDAxLmZhc3RxLmd6IikKICAJfQogIAogIAlmaWxlLnJlbmFtZShmcm9tPWZpbGUucGF0aChkYXRhLmRpcixmcm9tLmZpbGVuYW1lKSx0bz1maWxlLnBhdGgoZGF0YS5kaXIsdG8uZmlsZW5hbWUpKQogIAljYXQoImZyb209IixmaWxlLnBhdGgoZGF0YS5kaXIsZnJvbS5maWxlbmFtZSksIlxuIiwidG89IixmaWxlLnBhdGgoZGF0YS5kaXIsdG8uZmlsZW5hbWUpLCJcbiIsc2VwPSIiKSAgICAKICAgIAogIH0KfQpgYGAKCiMjIGNlbGwgcmFuZ2VyCgoKV2UgcnN5bmMgdGhlIGZhc3RxLmd6IGZpbGVzIG9udG8gZmFybmFtIHVuZGVyIH4vc2NyYXRjaDYwL1NBUkNfMTBYL2Zhc3RxIGFuZCBydW4gY2VsbCByYW5nZXIgb24gdGhlIGZpbGVzIHVzaW5nIGNvbW1hbmRzIGdlbmVyYXRlZCBieSB0aGUgZm9sbG93aW5nIGNvZGVzLgoKYGBge3J9CmlkLmxpc3Q8LXJlYWRMaW5lcygiL1VzZXJzL3lhbnhpdGluZy9Eb3dubG9hZHMvU1JSX0FjY19MaXN0KDIpLnR4dCIpCnRlbXAxPC1wYXN0ZSgiIyEvYmluL2Jhc2hcbiNTQkFUQ0ggLS10aW1lPTI0OjAwOjAwICAtLW50YXNrcz0xIC0tcGFydGl0aW9uPWdlbmVyYWwgLS1jcHVzLXBlci10YXNrPTggLS1tZW09ODBHQiAtLWpvYi1uYW1lPSIsaWQubGlzdCwiIC1vIC9ob21lL3h5NDgvc2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfc2NyaXB0cy8iLGlkLmxpc3QsIi5zaC5vJUogLWUgL2hvbWUveHk0OC9zY3JhdGNoNjAvU0FSQ18xMFgvY2VsbHJhbmdlcl9zY3JpcHRzLyIsaWQubGlzdCwiLnNoLmUlSlxuIixzZXA9IiIpCnRlbXAyPC1yZXAoIm1vZHVsZSBsb2FkIGNlbGxyYW5nZXIvNS4wLjBcbm1vZHVsZSBsb2FkIGJjbDJmYXN0cTIvMi0yMC0wLWZvc3MtMjAxOGJcbiIsbGVuZ3RoKGlkLmxpc3QpKQp0ZW1wMzwtcGFzdGUoImNkIC9ob21lL3h5NDgvc2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfcmVzdWx0c1xuIikKdGVtcDQ8LXBhc3RlKCJjZWxscmFuZ2VyIGNvdW50IC0taWQ9IixpZC5saXN0LCIgLS1mYXN0cXM9L2hvbWUveHk0OC9zY3JhdGNoNjAvU0FSQ18xMFgvZmFzdHEgLS10cmFuc2NyaXB0b21lPS9ob21lL3h5NDgvc2NyYXRjaDYwL1NBUkNfMTBYL3JlZmRhdGEtZ2V4LUdSQ2gzOC0yMDIwLUEgLS1sb2NhbGNvcmVzPTggLS1sb2NhbG1lbT02MCAtLXNhbXBsZT0iLGlkLmxpc3QsIlxuIixzZXA9IiIpCmNtZC5vdXQ8LWNiaW5kKHRlbXAxLHRlbXAyLHRlbXAzLHRlbXA0KQp0ZW1wLmNtZDwtYXBwbHkoY21kLm91dCwxLHBhc3RlLGNvbGxhcHNlPSIiLHNlcD0iIikKY2F0KHRlbXAuY21kLGZpbGU9Ii4vY2VsbHJhbmdlcl9zY3JpcHRzLnR4dCIsYXBwZW5kPUYsc2VwPSIiKQpgYGAKCgpUaGVyZSBhcmUgc2FtcGxlcyB3aXRoIG11bHRpcGxlIHJ1bnMuIFdlIHJ1biBjZWxscmFuZ2VyIGFnZ3IgdG8gbWVyZ2UgdGhlIG11bHRpcGxlIHJ1bnMgaW50byBvbmUgc2luZ2xlIG5VTUkgdmVjdG9yLgoKYGBge3J9CnNvdXJjZShmaWxlLnBhdGgoaG9tZS5kaXIsIlJwcm9ncmFtL215X2Z1bmN0aW9ucy5SIikpCmxpYnJhcnkoeGxzeCkKaG9tZS5kaXI8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0Zhcm5hbSIKcnVudGFibGUuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaDYwL1NBUkNfMTBYL1NyYVJ1blRhYmxlLnR4dCIpCmlkLmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2g2MC9TQVJDXzEwWC9zY1JOQV9pZHMueGxzeCIpCgpydW4udGFibGU8LXJlYWQudGFibGUocnVudGFibGUuZmlsZXBhdGgsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXMgPSBGKQppZC50YWJsZTwtcmVhZC54bHN4KGlkLmZpbGVwYXRoLHNoZWV0SW5kZXggPSAxLGNoZWNrLm5hbWVzPUYpCgp0ZW1wLnRhYmxlPC1ydW4udGFibGVbcnVuLnRhYmxlJGBHRU9fQWNjZXNzaW9uIChleHApYCVpbiVhcy5tYXRyaXgoaWQudGFibGUpWywxXSxdCnRlbXAubGlzdDwtc3BsaXQoYXMubWF0cml4KHRlbXAudGFibGUpWywxXSx0ZW1wLnRhYmxlJGBHRU9fQWNjZXNzaW9uIChleHApYCkKCiMgZXh0cmFjdCB0aGUgc2FtcGxlcyB3aXRoIG11bHRpcGxlIHJ1bnMKdGVtcC5saXN0PC10ZW1wLmxpc3RbdW5saXN0KGxhcHBseSh0ZW1wLmxpc3QsbGVuZ3RoKSk+MV0KCiMgZmluZCBvdXQgdGhlIGZhc3RxIHBhcnQgdGhlc2UgSURzIGJlbG9uZyB0bwp0ZW1wLmZpbGVuYW1lczwtbGlzdC5maWxlcyhmaWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2g2MC9TQVJDXzEwWCIpKQp0ZW1wLmZpbGVuYW1lczwtdGVtcC5maWxlbmFtZXNbZ3JlcCgiLmZpbGVsaXN0Iix0ZW1wLmZpbGVuYW1lcyldCnRlbXAuZmlsZW5hbWVzPC10ZW1wLmZpbGVuYW1lc1tncmVwKCJmYXN0cSIsdGVtcC5maWxlbmFtZXMpXQoKZmlsZS5saXN0PC1saXN0KCkKZm9yKGkgaW4gMTpsZW5ndGgodGVtcC5maWxlbmFtZXMpKXsKICB0ZW1wPC1yZWFkTGluZXMoZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoNjAvU0FSQ18xMFgiLHRlbXAuZmlsZW5hbWVzW2ldKSkKICB0ZW1wPC11bm5hbWUoc2FwcGx5KHRlbXAsbXkuZWxlbWVudC5leHRyYWN0LHNwbGl0Y2hhcj0iLyIsaW5kZXg9LTEpKQogIHRlbXA8LXVubmFtZShzYXBwbHkodGVtcCxteS5lbGVtZW50LmV4dHJhY3Qsc3BsaXRjaGFyPSJfIixpbmRleD0xKSkKICB0ZW1wPC11bmlxdWUodGVtcCkKICBmaWxlLmxpc3RbW2ldXTwtdGVtcAp9Cm5hbWVzKGZpbGUubGlzdCk8LXRlbXAuZmlsZW5hbWVzCnRlbXA8LWNoYXJhY3RlcigpCmZvcihpIGluIDE6bGVuZ3RoKGZpbGUubGlzdCkpewogIHRlbXA8LWModGVtcCxyZXAobmFtZXMoZmlsZS5saXN0KVtpXSxsZW5ndGgoZmlsZS5saXN0W1tpXV0pKSkKfQoKZmlsZS5saXN0Lm1hdHJpeDwtY2JpbmQodW5saXN0KGZpbGUubGlzdCksdGVtcCkKCnRlbXA8LWxpc3QoKQpmb3IoaSBpbiAxOmxlbmd0aCh0ZW1wLmxpc3QpKXsKICB0ZW1wW1tpXV08LXVuaXF1ZShmaWxlLmxpc3QubWF0cml4W2ZpbGUubGlzdC5tYXRyaXhbLDFdJWluJXRlbXAubGlzdFtbaV1dLDJdKQp9CgojIHRyYW5zZmVyIHRoZSBjZWxscmFuZ2VyIHJlc3VsdHMgYmFjayB0byBmYXJuYW0KIyBnZW5lcmF0ZSB0aGUgYWdncmVnYXRpb24gQ1NWIGZpbGUKY2VsbHJhbmdlci5kaXI8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfcmVzdWx0cyIpCm91dHB1dC5kaXI8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfYWdncl9zY3JpcHRzIikKCmZvcihpIGluIDE6bGVuZ3RoKHRlbXAubGlzdCkpewogIG91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUobmFtZXModGVtcC5saXN0KVtpXSwiX2FnZ3IuY3N2IixzZXA9IiIpKQogICMgcGlwZWxpbmUgYmVmb3JlIGNlbGxyYW5nZXIgNi4wLCB1c2UgbGlicmFyeS4gT3RoZXJ3aXNlLCB1c2Ugc2FtcGxlX2lkCiAgY21kLm91dDwtYygibGlicmFyeV9pZCIsIm1vbGVjdWxlX2g1IikKICBjbWQub3V0PC1yYmluZChjbWQub3V0LGNiaW5kKHRlbXAubGlzdFtbaV1dLHBhc3RlKCIvaG9tZS94eTQ4L3NjcmF0Y2g2MC9TQVJDXzEwWC9jZWxscmFuZ2VyX3Jlc3VsdHMvIix0ZW1wLmxpc3RbW2ldXSwiL291dHMvbW9sZWN1bGVfaW5mby5oNSIsc2VwPSIiKSkpCiAgd3JpdGUudGFibGUoY21kLm91dCxmaWxlPW91dHB1dC5maWxlcGF0aCxyb3cubmFtZXM9Rixjb2wubmFtZXM9RixzZXA9IiwiLGFwcGVuZD1GLHF1b3RlPUYpCn0KCiMgZ2VuZXJhdGUgdGhlIHNoIGZpbGUgdG8gcnVuIGNlbGxyYW5nZXIgYWdnciBvbiB0aGUgcmVwbGljYXRlZCBydW5zIG9mIHRoZSBzYW1lIHNhbXBsZS4Kc2NyaXB0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoNjAvU0FSQ18xMFgvY2VsbHJhbmdlcl9hZ2dyX3NjcmlwdHMiKQpyZXN1bHQuZGlyPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2g2MC9TQVJDXzEwWC9jZWxscmFuZ2VyX2FnZ3JfcmVzdWx0cyIpCgpmb3IoaSBpbiAxOmxlbmd0aCh0ZW1wLmxpc3QpKXsKICBjc3YuZmlsZXBhdGg8LWZpbGUucGF0aChzY3JpcHQuZGlyLHBhc3RlKG5hbWVzKHRlbXAubGlzdClbaV0sIl9hZ2dyLmNzdiIsc2VwPSIiKSkKICBzY3JpcHQuZmlsZXBhdGg8LWZpbGUucGF0aChzY3JpcHQuZGlyLHBhc3RlKG5hbWVzKHRlbXAubGlzdClbaV0sIi5zaCIsc2VwPSIiKSkKICBjbWQub3V0PC1wYXN0ZSgiIyEvYmluL2Jhc2hcbiNTQkFUQ0ggLS10aW1lPTI0OjAwOjAwICAtLW50YXNrcz0xIC0tcGFydGl0aW9uPWdlbmVyYWwgLS1jcHVzLXBlci10YXNrPTEgLS1tZW09ODBHQiAtLWpvYi1uYW1lPSIsbmFtZXModGVtcC5saXN0KVtpXSwiIC1vIC9ob21lL3h5NDgvc2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfYWdncl9zY3JpcHRzLyIsbmFtZXModGVtcC5saXN0KVtpXSwiLnNoLm8lSiAtZSAvaG9tZS94eTQ4L3NjcmF0Y2g2MC9TQVJDXzEwWC9jZWxscmFuZ2VyX2FnZ3Jfc2NyaXB0cy8iLG5hbWVzKHRlbXAubGlzdClbaV0sIi5zaC5lJUpcbiIsc2VwPSIiKQogIGNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIm1vZHVsZSBsb2FkIGNlbGxyYW5nZXIvNS4wLjBcbm1vZHVsZSBsb2FkIGJjbDJmYXN0cTIvMi0yMC0wLWZvc3MtMjAxOGJcbiIsc2VwPSIiKQogIGNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsImNkIC9ob21lL3h5NDgvc2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfYWdncl9yZXN1bHRzXG4iLHNlcD0iIikKICBjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJjZWxscmFuZ2VyIGFnZ3IgLS1pZD0iLG5hbWVzKHRlbXAubGlzdClbaV0sIiAtLWNzdj0vaG9tZS94eTQ4L3NjcmF0Y2g2MC9TQVJDXzEwWC9jZWxscmFuZ2VyX2FnZ3Jfc2NyaXB0cy8iLG5hbWVzKHRlbXAubGlzdClbaV0sIl9hZ2dyLmNzdlxuIixzZXA9IiIpCiAgY2F0KGNtZC5vdXQsZmlsZT1zY3JpcHQuZmlsZXBhdGgsYXBwZW5kPUYpCn0KCmBgYAoKV2UgbW92ZWQgdGhlIGNlbGxyYW5nZXIgYWdnciByZXN1bHRzIGJhY2sgdG8gY2VsbHJhbmdlcl9yZXN1bHRzIHRvZ2V0aGVyIHdpdGggdGhlIHNhbXBsZXMgd2l0aCB1bmlxdWUgcnVuLgoKIyMgVU1JIG1hdHJpeCBleHRyYWN0aW9uCgpXZSBleHRyYWN0IHRoZSBuVU1JIHZlY3RvciBmb3IgdGhlIHNhbXBsZXMgaW5jbHVkZWQgaW4gdGhlIG9yaWdpbmFsIHBhcGVyLgoKYGBge3J9CmhvbWUuZGlyPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9GYXJuYW0iCnNvdXJjZShmaWxlLnBhdGgoaG9tZS5kaXIsIlJwcm9ncmFtL215X2Z1bmN0aW9ucy5SIikpCmxpYnJhcnkoeGxzeCkKcnVudGFibGUuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaDYwL1NBUkNfMTBYL1NyYVJ1blRhYmxlLnR4dCIpCmlkLmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2g2MC9TQVJDXzEwWC9zY1JOQV9pZHMueGxzeCIpCgpydW4udGFibGU8LXJlYWQudGFibGUocnVudGFibGUuZmlsZXBhdGgsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXMgPSBGKQppZC50YWJsZTwtcmVhZC54bHN4KGlkLmZpbGVwYXRoLHNoZWV0SW5kZXggPSAxLGNoZWNrLm5hbWVzPUYpCgpteS50YWJsZTwtcnVuLnRhYmxlW3J1bi50YWJsZSRgR0VPX0FjY2Vzc2lvbiAoZXhwKWAlaW4lYXMubWF0cml4KGlkLnRhYmxlKVssMV0sXQoKZmlsZW5hbWVzPC1saXN0LmZpbGVzKGZpbGUucGF0aChob21lLmRpciwic2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfcmVzdWx0cyIpKQoKbWVyZ2VkLmRhdGE8LW51bWVyaWMoKQpmb3IoaSBpbiAxOmxlbmd0aChmaWxlbmFtZXMpKXsKICBpZihzdWJzdHIoZmlsZW5hbWVzW2ldLDEsMSk9PSJHIil7CiAgICBtYXRyaXguZGlyPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2g2MC9TQVJDXzEwWC9jZWxscmFuZ2VyX3Jlc3VsdHMiLGZpbGVuYW1lc1tpXSwib3V0cyIsImNvdW50IiwiZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXgiKQogIH1lbHNlewogICAgbWF0cml4LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoNjAvU0FSQ18xMFgvY2VsbHJhbmdlcl9yZXN1bHRzIixmaWxlbmFtZXNbaV0sIm91dHMiLCJmaWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeCIpCiAgfQogIAogIGJhcmNvZGUucGF0aCA8LSBmaWxlLnBhdGgobWF0cml4LmRpciwgImJhcmNvZGVzLnRzdi5neiIpCiAgZmVhdHVyZXMucGF0aCA8LSBmaWxlLnBhdGgobWF0cml4LmRpciwgImZlYXR1cmVzLnRzdi5neiIpCiAgbWF0cml4LnBhdGggPC0gZmlsZS5wYXRoKG1hdHJpeC5kaXIsICJtYXRyaXgubXR4Lmd6IikKICBtYXQgPC0gcmVhZE1NKGZpbGUgPSBtYXRyaXgucGF0aCkKICBmZWF0dXJlLm5hbWVzID0gcmVhZC5kZWxpbShmZWF0dXJlcy5wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICBiYXJjb2RlLm5hbWVzID0gcmVhZC5kZWxpbShiYXJjb2RlLnBhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogIGlmKHN1YnN0cihmaWxlbmFtZXNbaV0sMSwxKT09IkciKXsKICAgIHNhbXBsZS5uYW1lPC1maWxlbmFtZXNbaV0KICB9ZWxzZXsKICAgIHNhbXBsZS5uYW1lPC1hcy5tYXRyaXgobXkudGFibGUpW215LnRhYmxlJFJ1bj09ZmlsZW5hbWVzW2ldLCJTYW1wbGUgTmFtZSJdCiAgfQogIAogIGNvbG5hbWVzKG1hdCkgPSBwYXN0ZTAoc2FtcGxlLm5hbWUsIl8iLGJhcmNvZGUubmFtZXMkVjEpCiAgcm93bmFtZXMobWF0KSA9IGZlYXR1cmUubmFtZXMkVjEKICAKICBpZihpPT0xKXsKICAgIG1lcmdlZC5kYXRhPC1tYXQKICAgIGdlbmUubmFtZXM8LXJvd25hbWVzKG1hdCkKICB9ZWxzZXsKICAgIG1lcmdlZC5kYXRhPC1jYmluZChtZXJnZWQuZGF0YSxtYXRbZ2VuZS5uYW1lcyxdKQogIH0KfQoKb3V0cHV0LmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL2RyaXZlcl9GYXJuYW0vc2NyYXRjaDYwL1NBUkNfMTBYL21lcmdlZF9uVU1JXzQ1LnJkcyIKc2F2ZVJEUyhtZXJnZWQuZGF0YSxmaWxlPW91dHB1dC5maWxlcGF0aCxyZWZob29rPU5VTEwpCgpgYGAKCldlIHRyYW5zZmVycmVkIHRoZSBjZWxscmFuZ2VyIHJlc3VsdHMgYmFjayB0byBmYXJuYW0gdG8gZXh0cmFjdCB0aGUgblVNSSByZXN1bHRzLiBXZSBhbHNvIGFkZCB0aGUgc2FtcGxlcyB3aXRoIG11bHRpcGxlIHJ1bnMgaW50byB0aGUgbWVyZ2VkIG1hdHJpeC4KCmBgYHtyIGV2YWw9RkFMU0V9CiMgc2FtcGxlcyB3aXRoIG11bHRpcGxlIHJ1bnMgaGF2ZSBuYW1lcyBvZiB0aGUgR0VPIGFjY2Vzc2lvbiBudW1iZXIuCnNvdXJjZSgiL2hvbWUveWFueGl0aW5nL2RyaXZlcl9GYXJuYW0vUnByb2dyYW0vbXlfZnVuY3Rpb25zLlIiKQpsaWJyYXJ5KE1hdHJpeCkKY2VsbHJhbmdlci5kaXI8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0Zhcm5hbS9zY3JhdGNoNjAvU0FSQ18xMFgvY2VsbHJhbmdlcl9yZXN1bHRzIgpydW50YWJsZS5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfRmFybmFtL3NjcmF0Y2g2MC9TQVJDXzEwWC9TcmFSdW5UYWJsZS50eHQiCgojIGxvYWQgaW4gdGhlIHJ1biB0YWJsZQpydW4udGFibGU8LXJlYWQudGFibGUocnVudGFibGUuZmlsZXBhdGgsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXM9RixzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIGxvYWQgaW4gdGhlIHJ1biBuYW1lcyBhbmQgdGhlIEdTTSBJRHMgdW5kZXIgdGhlIGNlbGwgcmFuZ2VyIHJlc3VsdHMgZm9sZGVyCnRlbXAuZmlsZW5hbWVzPC1saXN0LmZpbGVzKGNlbGxyYW5nZXIuZGlyKQoKc2FtcGxlLnJlcDwtdGVtcC5maWxlbmFtZXNbc3Vic3RyKHRlbXAuZmlsZW5hbWVzLDEsMyk9PSJHU00iXQpzYW1wbGUudW5pPC10ZW1wLmZpbGVuYW1lc1tzdWJzdHIodGVtcC5maWxlbmFtZXMsMSwzKSE9IkdTTSJdCm15LnJ1bi50YWJsZTwtcnVuLnRhYmxlW2FzLm1hdHJpeChydW4udGFibGUpWywiU2FtcGxlIE5hbWUiXSVpbiVzYW1wbGUucmVwIHwgYXMubWF0cml4KHJ1bi50YWJsZSlbLCJSdW4iXSVpbiVzYW1wbGUudW5pLF0KIyBleHRyYWN0IHRoZSBuVU1JIG1hdHJpeApkaXJuYW1lczwtbGlzdC5maWxlcyhjZWxscmFuZ2VyLmRpcikKZGlybmFtZXM8LWRpcm5hbWVzW3N1YnN0cihkaXJuYW1lcywxLDQpPT0iU1JSMSJdCgpmb3IoaSBpbiAxOmxlbmd0aChkaXJuYW1lcykpewogIHJ1bi5uYW1lPC1kaXJuYW1lc1tpXQogIHNhbXBsZS5uYW1lPC1ydW4udGFibGVbcnVuLnRhYmxlWywiUnVuIl09PXJ1bi5uYW1lLCJTYW1wbGUgTmFtZSJdCiAgbWF0cml4X2RpcjwtZmlsZS5wYXRoKGNlbGxyYW5nZXIuZGlyLGRpcm5hbWVzW2ldLCJvdXRzIiwiZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXgiKQogIGJhcmNvZGUucGF0aCA8LSBmaWxlLnBhdGgobWF0cml4X2RpciwgImJhcmNvZGVzLnRzdi5neiIpCiAgZmVhdHVyZXMucGF0aCA8LSBmaWxlLnBhdGgobWF0cml4X2RpciwgImZlYXR1cmVzLnRzdi5neiIpCiAgbWF0cml4LnBhdGggPC0gZmlsZS5wYXRoKG1hdHJpeF9kaXIsICJtYXRyaXgubXR4Lmd6IikKICBtYXQgPC0gcmVhZE1NKGZpbGUgPSBtYXRyaXgucGF0aCkKICBmZWF0dXJlLm5hbWVzID0gcmVhZC5kZWxpbShmZWF0dXJlcy5wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICBiYXJjb2RlLm5hbWVzID0gcmVhZC5kZWxpbShiYXJjb2RlLnBhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogIGNvbG5hbWVzKG1hdCkgPSBwYXN0ZTAoc2FtcGxlLm5hbWUsIl8iLGJhcmNvZGUubmFtZXMkVjEpCiAgcm93bmFtZXMobWF0KSA9IGZlYXR1cmUubmFtZXMkVjEKICAKICBvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aCgiL2hvbWUveWFueGl0aW5nL2RyaXZlcl9GYXJuYW0vc2NyYXRjaDYwL1NBUkNfMTBYL2NlbGxyYW5nZXJfcmVzdWx0cyIscGFzdGUwKHNhbXBsZS5uYW1lLCJfIixydW4ubmFtZSwiX25VTUkucmRzIikpCiAgc2F2ZVJEUyhtYXQsZmlsZT1vdXRwdXQuZmlsZXBhdGgscmVmaG9vaz1OVUxMKQp9CgpgYGAKCgpTaW5jZSB3ZSBoYXZlIGxpbWl0ZWQgc3RvcmFnZSBvbiB0aGUgaHBjLCB3ZSBiYWNrIHVwIHRoZSBmYXN0cS5neiBmaWxlcyBhbmQgdGhlIGNlbGwgcmFuZ2VyIG91dHB1dCBmb2xkZXJzLiBXZSBleHRyYWN0ZWQgdGhlIG5VTUkgbWF0cml4IG9mIGVhY2ggc2FtcGxlIHNlcGFyYXRlbHkgYW5kIGRlbGV0ZWQgYWxsIG90aGVyIGZpbGVzIHRvIG1ha2Ugcm9vbS4gQWxsIGZhc3RxLmd6IGZpbGVzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIGNlbGwgcmFuZ2VyIG91dHB1dCBmb2xkZXJzIGFyZSBiYWNrZWQgdXAgb24gZ29vZ2xlIGRyaXZlIHVuZGVyIC9Wb2x1bWVzL0dvb2dsZURyaXZlL015IERyaXZlL0dyYWNlQmFja3VwL0dSQURTL1NBUkNfUEJNQy9TQVJDXzEwWC4KCiMjIFNldXJhdCBBbmFseXNpcwoKV2UgbG9hZCB0aGUgZGF0YSBpbnRvIFNldXJhdCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcywgZXNwZWNpYWxseSBmb3IgdGhlIGludGVncmF0ZWQgYW5hbHlzaXMuCgpGaXJzdCwgd2UgY3JlYXRlIHRoZSBTZXVyYXQgb2JqZWN0IGFuZCBwcmVsaW1pbmFyeSBmaWx0ZXJpbmcgb24gZ2VuZXMgYW5kCmBgYHtyfQojIGR1ZSB0byB0aGUgdW5hdmFpbGFiaWxpdHkgb2YgdGhlIGNsdXN0ZXIsIHdlIHJhbiB0aGVzZSBjb2RlcyBvbiB0YWM0IHNlcnZlciB3aXRoIHRoZSBzYW1lIHZlcnNpb25zIG9mIFIgYW5kIHBhY2thZ2VzCiMgc3J1biAtLXB0eSAtLXgxMSAtcCBwaV9rYW1pbnNraSAtdCAxMjowMDowMCAtLW50YXNrcz0xIC0tbm9kZXM9MSAtLWNwdXMtcGVyLXRhc2s9MSAtLW1lbT02MDE1MiBiYXNoCiMgbW9kdWxlIHJlc3RvcmUgc2V1cmF0CiNob21lLmRpcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfRmFybmFtIgpob21lLmRpcjwtIi9ob21lL3lhbnhpdGluZy9Eb2N1bWVudHMvUmVzZWFyY2gvR1JBRFNfU0FSQ19QQk1DIgpzb3VyY2UoIi9ob21lL3lhbnhpdGluZy9ScHJvZ3JhbS9teV9mdW5jdGlvbnMuUiIpCgojb3V0cHV0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoNjAvU0FSQ18xMFgvU2V1cmF0IikKb3V0cHV0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY1JOQS1zZXEvU2V1cmF0IikKCmlmKGZpbGUuZXhpc3RzKG91dHB1dC5kaXIpPT1GKXsKZGlyLmNyZWF0ZShvdXRwdXQuZGlyKQp9CgojIExvYWQgdGhlIG1lcmdlZCBkYXRhCm1lcmdlZC5kYXRhPC1yZWFkUkRTKGZpbGUucGF0aChob21lLmRpciwiRGF0YS9tZXJnZWRfblVNSV80NS5yZHMiKSxyZWZob29rPU5VTEwpCgojIGxvYWQgaW4gdGhlIGdlbmVzLmd0ZiB0byBjb252ZXJ0IGVuc2VtYmwgSUQgdG8gZ2VuZSBuYW1lcwpndGYuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0Zhcm5hbS9zY3JhdGNoNjAvU0FSQ18xMFgvcmVmZGF0YS1nZXgtR1JDaDM4LTIwMjAtQS9nZW5lcy9nZW5lcy5ndGYiCmd0Zi5tYXRyaXg8LXJlYWQudGFibGUoZ3RmLmZpbGVwYXRoLHNlcD0iXHQiLGhlYWRlcj1GLGNoZWNrLm5hbWVzID0gRiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLHNraXAgPSA1KQpndGYubWF0cml4PC1ndGYubWF0cml4W2d0Zi5tYXRyaXhbLDNdPT0iZ2VuZSIsXQp0ZW1wPC1ndGYubWF0cml4Wyw5XQoKZ2VuZS5pZDwtdW5uYW1lKHNhcHBseShzYXBwbHkodGVtcCxteS5lbGVtZW50LmV4dHJhY3Qsc3BsaXRjaGFyPSI7ICIsaW5kZXg9MSksbXkuZWxlbWVudC5leHRyYWN0LHNwbGl0Y2hhcj0iICIsaW5kZXg9LTEpKQpnZW5lLm5hbWU8LXVubmFtZShzYXBwbHkoc2FwcGx5KHRlbXAsbXkuZWxlbWVudC5leHRyYWN0LHNwbGl0Y2hhcj0iOyAiLGluZGV4PTQpLG15LmVsZW1lbnQuZXh0cmFjdCxzcGxpdGNoYXI9IiAiLGluZGV4PS0xKSkKZ2VuZS5saXN0PC1zcGxpdChnZW5lLmlkLGdlbmUubmFtZSkKIyBnZXQgdGhlIGxpc3Qgb2YgZW5zZW1ibCBJRHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzCm10LmdlbmVzPC11bmxpc3QoZ2VuZS5saXN0W2dyZXAoIl5NVC0iLG5hbWVzKGdlbmUubGlzdCkpXSkKCiNnZW9mZi5kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSBmaWxlLnBhdGgoZGF0YS5kaXIsIm91dHMiLCJmaWx0ZXJlZF9nZW5lX2JjX21hdHJpY2VzX21leCIsIkdSQ2gzOCIpKQojbG9hZChmaWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2hfa2FtaW5za2kvcHVibGljL0JhY2t1cC9Kb25hcy9SX29iamVjdHMvMTB4X0NodXBwQXN0aG1hLm10eC5oeWJyaWRfZ2VuZV9zeW1ib2xzX3dvX2JhY2tncm91bmRfMDNfMTExOS5Sb2JqIikpCmdjcWwxIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBtZXJnZWQuZGF0YSwgcHJvamVjdCA9ICJTQVJDXzEwWCIsbWluLmNlbGxzID0gMywgIG1pbi5mZWF0dXJlcyA9IDIwMCkKI2djcWwxW1sicGVyY2VudC5tdCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChnY3FsMSwgcGF0dGVybiA9ICJeTVQtIikKZ2NxbDFbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KGdjcWwxLCBmZWF0dXJlcyA9IG10LmdlbmVzKQoKY2F0KCJcbiIpCmNhdCgiT3JpZ2luYWxseSwgdGhlcmUgYXJlICIsbnJvdyhtZXJnZWQuZGF0YSksIiBnZW5lcyBhbmQgIixuY29sKG1lcmdlZC5kYXRhKSwiIGNlbGxzIGluIHRoZSBkYXRhLlxuIixzZXA9IiIpCmNhdCgiQWZ0ZXIgdGhlIGZpcnN0IHN0ZXAgZmlsdGVyaW5nLCB0aGVyZSBhcmUgIixucm93KGdjcWwxQGFzc2F5cyRSTkFAZGF0YSksIiBnZW5lcyBhbmQgIixuY29sKGdjcWwxQGFzc2F5cyRSTkFAZGF0YSksIiBjZWxscyBpbiB0aGUgZmlsdGVyZWQgZGF0YS5cbiIsc2VwPSIiKQoKcm0obWVyZ2VkLmRhdGEpCiNnYyh2ZXJib3NlPUYpCmludmlzaWJsZShnYygpKQoKIyBsb2FkIGluIHRoZSBwaGVub3R5cGUgZGF0YSBvZiBhbGwgdGhlIHNhbXBsZXMKaWQuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwiRGF0YS9zY1JOQV9pZHMueGxzeCIpCnJ1bnRhYmxlLmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsIkRhdGEvU3JhUnVuVGFibGUudHh0IikKCnJ1bi50YWJsZTwtcmVhZC50YWJsZShydW50YWJsZS5maWxlcGF0aCxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcyA9IEYpCmlkLnRhYmxlPC1yZWFkLnhsc3goaWQuZmlsZXBhdGgsc2hlZXRJbmRleCA9IDEsY2hlY2submFtZXM9RikKCiMgZ2V0IHRoZSBzYW1wbGUgbmFtZXMgZm9yIGFsbCB0aGUgY2VsbHMKbXkuaWRlbnQ8LXVubmFtZShzYXBwbHkoY29sbmFtZXMoZ2NxbDFAYXNzYXlzJFJOQUBjb3VudHMpLG15LmVsZW1lbnQuZXh0cmFjdCxzcGxpdGNoYXI9Il8iLGluZGV4PTEpKQpuYW1lcyhteS5pZGVudCk8LWNvbG5hbWVzKGdjcWwxQGFzc2F5cyRSTkFAY291bnRzKQoKdGVtcDwtc3BsaXQoYXMubWF0cml4KHJ1bi50YWJsZSlbLCJzdHVkeV9jbGFzc2lmaWNhdGlvbiJdLGFzLm1hdHJpeChydW4udGFibGUpWywiU2FtcGxlIE5hbWUiXSkKdGVtcDwtbGFwcGx5KHRlbXAsdW5pcXVlKQpteS5kaXNlYXNlPC11bmxpc3QodGVtcFtteS5pZGVudF0pCgpteS5zYW1wbGVuYW1lczwtbXkuaWRlbnQKZGlzZWFzZS5zYW1wbGVuYW1lczwtcGFzdGUobXkuZGlzZWFzZSwiXyIsbXkuc2FtcGxlbmFtZXMsc2VwPSIiKQoKIyBzY1JOQS1zZXEgUUMgbWV0cmljLgojbWl0by5nZW5lczEgPC0gZ3JlcChwYXR0ZXJuID0gIl5NVC0iLCB4ID0gcm93bmFtZXMoZ2NxbDFAYXNzYXlzJFJOQUBjb3VudHMpLCB2YWx1ZSA9IFRSVUUpCiNwZXJjZW50Lm1pdG8xIDwtIE1hdHJpeDo6Y29sU3VtcyhnY3FsMUBhc3NheXMkUk5BQGNvdW50c1ttaXRvLmdlbmVzMSwgXSkvTWF0cml4Ojpjb2xTdW1zKGdjcWwxQGFzc2F5cyRSTkFAY291bnRzKQoKIyBBZGRNZXRhRGF0YSBhZGRzIGNvbHVtbnMgdG8gb2JqZWN0QG1ldGEuZGF0YSwgYW5kIGlzIGEgZ3JlYXQgcGxhY2UgdG8KIyBzdGFzaCBRQyBzdGF0cwpnY3FsMSA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBnY3FsMSwgbWV0YWRhdGEgPSBteS5kaXNlYXNlLCBjb2wubmFtZSA9ICJkaXNlYXNlIikKZ2NxbDEgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gZ2NxbDEsIG1ldGFkYXRhID0gbXkuc2FtcGxlbmFtZXMsIGNvbC5uYW1lID0gInNhbXBsZS5uYW1lcyIpCmdjcWwxIDwtIEFkZE1ldGFEYXRhKG9iamVjdCA9IGdjcWwxLCBtZXRhZGF0YSA9IG15LmlkZW50LCBjb2wubmFtZSA9ICJzYW1wbGUiKQpnY3FsMSA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBnY3FsMSwgbWV0YWRhdGEgPSBkaXNlYXNlLnNhbXBsZW5hbWVzLCBjb2wubmFtZSA9ICJkaXNlYXNlLnNhbXBsZW5hbWVzIikKdGVtcDwtcmVwKCJwYm1jIixucm93KGdjcWwxQG1ldGEuZGF0YSkpCmdjcWwxIDwtIEFkZE1ldGFEYXRhKG9iamVjdCA9IGdjcWwxLCBtZXRhZGF0YSA9IHRlbXAsIGNvbC5uYW1lID0gInRpc3N1ZSIpCgojIGZpbHRlciB0aGUgZGF0YSB0byBvbmx5IGtlZXAgZ2VuZXMgcHJlc2VudCAoPjAgaW4gPj0xJSBvZiBhbGwgdGhlIGNlbGxzKSBpbiA+PTMgc3ViamVjdHMKZ2VuZS5wcmVzPC1udW1lcmljKCkKc2FtcGxlLm5hbWVzLnVuaXF1ZTwtdW5pcXVlKGdjcWwxQG1ldGEuZGF0YSRzYW1wbGUubmFtZXMpCgpmb3IoaSBpbiAxOmxlbmd0aChzYW1wbGUubmFtZXMudW5pcXVlKSl7CiAgdGVtcC5tYXRyaXg8LWdjcWwxQGFzc2F5cyRSTkFAY291bnRzWyxnY3FsMUBtZXRhLmRhdGEkc2FtcGxlLm5hbWVzPT1zYW1wbGUubmFtZXMudW5pcXVlW2ldXQogIHRlbXAudmVjdDwtYXBwbHkodGVtcC5tYXRyaXg+MCwxLHN1bSkKICBnZW5lLnByZXM8LWNiaW5kKGdlbmUucHJlcyx0ZW1wLnZlY3Q+PShuY29sKHRlbXAubWF0cml4KSowLjAxKSkKfQoKZ2VuZS5uYW1lczwtcm93bmFtZXMoZ2NxbDFAYXNzYXlzJFJOQUBjb3VudHMpW2FwcGx5KGdlbmUucHJlcywxLHN1bSk+PTNdCmdjcWwxLm9yaWc8LWdjcWwxCmdjcWwxPC1zdWJzZXQoZ2NxbDEub3JpZyxmZWF0dXJlcz1nZW5lLm5hbWVzKQoKY2F0KCJBZnRlciB0aGUgZmlyc3QgYW5kIHNlY29uZCBzdGVwcyBvZiBmaWx0ZXJpbmcsIHRoZXJlIGFyZSAiLG5yb3coZ2NxbDFAYXNzYXlzJFJOQUBjb3VudHMpLCIgZ2VuZXMgYW5kICIsbmNvbChnY3FsMUBhc3NheXMkUk5BQGNvdW50cyksIiBjZWxscyBpbiB0aGUgZGF0YS5cbiIsc2VwPSIiKQpgYGAKCgpTZWNvbmQsIHdlIGhhZCBhIHRoaXJkIGZpbHRlcmluZyBzdGVwIG9uIGNlbGxzIGJhc2VkIG9uIHRoZSBuR2VuZSBhbmQgcGVyY2VudC5taXRvLiBIZXJlIGFyZSB0aGUgcGxvdHMgdG8gaGVscCB1cyBkZWNpZGUgdGhlIHRocmVzaG9sZCBvZiB0aGUgZmlsdGVyaW5nLgpgYGB7ciBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMCxjYWNoZT1GQUxTRX0KCiNnMTwtVmxuUGxvdChvYmplY3QgPSBnY3FsMSwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAzLGdyb3VwLmJ5PSJzYW1wbGVuYW1lcyIsY29tYmluZT1UKQoKZzI8LVZsblBsb3Qob2JqZWN0ID0gZ2NxbDEsIGZlYXR1cmVzPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAzLGdyb3VwLmJ5ID0gImRpc2Vhc2Uuc2FtcGxlbmFtZXMiLGNvbWJpbmU9VCkKZ3JpZC5hcnJhbmdlKGcyLG5jb2w9MSkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTE0LGZpZy5oZWlnaHQ9NCxlY2hvPUZBTFNFLGNhY2hlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQojIHNob3cgdGhlIDJEIHNjYXR0ZXJwbG90CmcxPC1GZWF0dXJlU2NhdHRlcihnY3FsMSxmZWF0dXJlMT0ibkNvdW50X1JOQSIsZmVhdHVyZTI9InBlcmNlbnQubXQiLGdyb3VwLmJ5ID0gInRpc3N1ZSIscHQuc2l6ZT0wLjUpCmcxPC1nMStnZW9tX2FibGluZShpbnRlcmNlcHQ9MTAsc2xvcGU9MCxjb2w9InJlZCIpIAoKZzI8LUZlYXR1cmVTY2F0dGVyKGdjcWwxLGZlYXR1cmUxPSJuQ291bnRfUk5BIixmZWF0dXJlMj0ibkZlYXR1cmVfUk5BIixncm91cC5ieSA9ICJ0aXNzdWUiLHB0LnNpemU9MC41KQpnMjwtZzIrZ2VvbV9hYmxpbmUoaW50ZXJjZXB0PTMwMCxzbG9wZT0wLGNvbD0icmVkIikKZzI8LWcyK2dlb21fYWJsaW5lKGludGVyY2VwdD02MDAwLHNsb3BlPTAsY29sPSJyZWQiKQoKZzM8LUZlYXR1cmVTY2F0dGVyKGdjcWwxLGZlYXR1cmUxPSJuRmVhdHVyZV9STkEiLGZlYXR1cmUyPSJwZXJjZW50Lm10Iixncm91cC5ieSA9ICJ0aXNzdWUiLHB0LnNpemU9MC41KQpnMzwtZzMrZ2VvbV9hYmxpbmUoaW50ZXJjZXB0PTEwLHNsb3BlPTAsY29sPSJyZWQiKQpnMzwtZzMrZ2VvbV92bGluZSh4aW50ZXJjZXB0PTMzMCxjb2w9InJlZCIpCmczPC1nMytnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9NjAwMCxjb2w9InJlZCIpCmc8LWdyaWQuYXJyYW5nZShnMSxnMixnMyxuY29sPTMpCmdnc2F2ZShmaWxlbmFtZT1maWxlLnBhdGgob3V0cHV0LmRpciwiMV9kYXRhcHJlcHJvY2Vzc2luZ19GZWF0dXJlU2NhdHRlci5wZGYiKSxwbG90PWcsZGV2aWNlPSJwZGYiLHVuaXRzID0gImluIix3aWR0aD0xNCxoZWlnaHQ9NCkKYGBgCgpgYGB7cn0KIyBvbmx5IGtlZXAgY2VsbHMgd2l0aCBuRmVhdHVyZV9STkE+PTMzMCBvciA8PTYwMDAKI2djcWwxPC1zdWJzZXQoZ2NxbDEsc3Vic2V0PSBuRmVhdHVyZV9STkEgPj0gMzAwICYgbkZlYXR1cmVfUk5BPD0gNjAwMCAmIHBlcmNlbnQubXQ8PSA1MCkKZ2NxbDE8LXN1YnNldChnY3FsMSxzdWJzZXQ9IG5GZWF0dXJlX1JOQSA+PSAzMDAgJiBwZXJjZW50Lm10PD0gNTApCmNhdCgiQWZ0ZXIgdGhlIHRoaXJkIGZpbHRlcmluZyBzdGVwLCB3ZSBoYXZlICIsbnJvdyhnY3FsMUBhc3NheXMkUk5BQGNvdW50cyksIiBnZW5lcyBhbmQgIixuY29sKGdjcWwxQGFzc2F5cyRSTkFAY291bnRzKSwiIGNlbGxzLlxuIixzZXA9IiIpCmBgYApCYXNlZCBvbiB0aGUgcXVhbGl0eSBjb250cm9sIHBsb3RzLCBpbiB0aGUgdGhpcmQgZmlsdGVyaW5nIHN0ZXAsIHdlIHJlbW92ZWQgY2VsbHMgd2l0aCA+NTAlIG1pdG9jaG9uZHJpYWwgcGVyY2VudGFnZSBvciAgPDMwMCBleHByZXNzZWQgZ2VuZXMuIEFmdGVyIHRoaXMgc2Vjb25kIGZpbHRlcmluZyBzdGVwLCB3ZSBoYXZlIGByIG5yb3coZ2NxbDFAYXNzYXlzJFJOQUBjb3VudHMpYCBnZW5lcyBhbmQgYHIgbmNvbChnY3FsMUBhc3NheXMkUk5BQGNvdW50cylgIGNlbGxzIHJlbWFpbmVkLiAKCgpUaGlyZCwgd2Ugbm9ybWFsaXplIHRoZSB1bmltcHV0ZWQgYW5kIHNhdmUgdGhlIFNBVkVSIGltcHV0ZWQgZGF0YSBhcyB0aGUgbm9ybWFsaXplZCBkYXRhLiBUaGVzZSBkYXRhIGFyZSBzYXZlZCBmb3IgZG93bnN0cmVhbSBmdXJ0aGVyIHByb2Nlc3NpbmcuCgpgYGB7cn0KIyBub3JtYWxpemUgdGhlIGRhdGEgYW5kIHNhdmUgdGhlIHJlc3VsdHMKZ2NxbDEgPC0gTm9ybWFsaXplRGF0YShnY3FsMSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgIHNjYWxlLmZhY3RvciA9IDEwMDAwKQpzYXZlUkRTKGdjcWwxLCBmaWxlID0gZmlsZS5wYXRoKG91dHB1dC5kaXIsIjFfbm9ybWFsaXplZF9kYXRhX3VuaW1wdXRlZF8xLnJkcyIpKQoKI2djcWwxLnNhdmVyPC1Ob3JtYWxpemVEYXRhKG9iamVjdCA9IGdjcWwxLnNhdmVyLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCAgc2NhbGUuZmFjdG9yID0gMTAwMDApCiNzYXZlUkRTKGdjcWwxLnNhdmVyLCBmaWxlID0gZmlsZS5wYXRoKG91dHB1dC5kaXIsInByZXByb2Nlc3NlZF9kYXRhX3NhdmVyaW1wdXRlZF8xLnJkcyIpKQoKY2F0KCJ0aGUgcHJlcHJvY2Vzc2VkIGRhdGEgZm9yIHRoZSB1bmltcHV0ZWQgZGF0YSB3YXMgc2F2ZWQgYXMgIixmaWxlLnBhdGgob3V0cHV0LmRpciwiMV9ub3JtYWxpemVkX2RhdGFfdW5pbXB1dGVkXzEucmRzIiksIlxuIixzZXA9IiIpCgpjYXQoIkFmdGVyIHRoZSBub3JtYWxpemF0aW9uLCB0aGVyZSBhcmUgIixucm93KGdjcWwxQGFzc2F5cyRSTkFAZGF0YSksIiBnZW5lcyBhbmQgIixuY29sKGdjcWwxQGFzc2F5cyRSTkFAZGF0YSksIiBjZWxsc1xuIixzZXA9IiIpCgojY2F0KCJ0aGUgcHJlcHJvY2Vzc2VkIGRhdGEgZm9yIHRoZSBTQVZFUiBpbXB1dGVkIGRhdGEgd2FzIHNhdmVkIGFzICIsZmlsZS5wYXRoKG91dHB1dC5kaXIsInByZXByb2Nlc3NlZF9kYXRhX3NhdmVyaW1wdXRlZF8xLnJkcyIpLCJcbiIsc2VwPSIiKQpgYGAKCiMjIyBPcmlnaW5hbCBDZWxsIENsdXN0ZXJpbmcKCkZpcnN0LCB3ZSBmaW5kIHZhcmlhYmxlIGdlbmVzLgpgYGB7cn0KZmVhdHVyZXMubjwtMjAwMApob21lLmRpcjwtIi9ob21lL3lhbnhpdGluZy9Eb2N1bWVudHMvUmVzZWFyY2gvR1JBRFNfU0FSQ19QQk1DIgpgYGAKCldlIGxvYWQgaW4gdGhlIG5vcm1hbGl6ZWQgdW5pbXB1dGVkIGRhdGEgYW5kIGZpbmQgdGhlIHRvcCBgciBmZWF0dXJlcy5uYCB2YXJpYWJsZSBnZW5lcyBmb3IgY2VsbCBjbHVzdGVyaW5nLiBUaGUgbm9ybWFsaXplZCBkYXRhIHdhcyBmdXJ0aGVyIHNjYWxlZCB0byByZW1vdmUgdGhlIGVmZmVjdCBvZiBuVU1JIGFuZCBwZXJjZW50Lm1pdG8gZm9yIGZ1cnRoZXIgY2VsbCBjbHVzdGVyaW5nIHB1cnBvc2UuCgpgYGB7ciBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQojIGxvYWQgaW4gdGhlIG5vcm1hbGl6ZWQgdW5pbXB1dGVkIGRhdGEKZGF0YS5maWxlcGF0aDwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY1JOQS1zZXEvU2V1cmF0LzFfbm9ybWFsaXplZF9kYXRhX3VuaW1wdXRlZF8xLnJkcyIpCm91dHB1dC5kaXI8LWZpbGUucGF0aChob21lLmRpciwic2NSTkEtc2VxL1NldXJhdCIpCgppZihmaWxlLmV4aXN0cyhvdXRwdXQuZGlyKT09Ril7CiAgZGlyLmNyZWF0ZShvdXRwdXQuZGlyKQp9CgpnY3FsMTwtcmVhZFJEUyhmaWxlID0gZGF0YS5maWxlcGF0aCwgcmVmaG9vayA9IE5VTEwpCmdjcWwxPC1GaW5kVmFyaWFibGVGZWF0dXJlcyhnY3FsMSxzZWxlY3Rpb24ubWV0aG9kPSJ2c3QiLG5mZWF0dXJlcz1mZWF0dXJlcy5uKQoKIyBJZGVudGlmeSB0aGUgMTAgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKdG9wMTAgPC0gaGVhZChWYXJpYWJsZUZlYXR1cmVzKGdjcWwxKSwgMTApCgojIHBsb3QgdmFyaWFibGUgZmVhdHVyZXMgd2l0aCBhbmQgd2l0aG91dCBsYWJlbHMKcGxvdDEgPC0gVmFyaWFibGVGZWF0dXJlUGxvdChnY3FsMSkKcGxvdDIgPC0gTGFiZWxQb2ludHMocGxvdCA9IHBsb3QxLCBwb2ludHMgPSB0b3AxMCwgcmVwZWwgPSBUUlVFKQpDb21iaW5lUGxvdHMocGxvdHMgPSBsaXN0KHBsb3QxLCBwbG90MikpCmBgYAoKClRoZW4gd2Ugc2NhbGUgdGhlIGRhdGEgdG8gcmVtb3ZlIHRoZSBlZmZlY3QgZnJvbSBuVU1JIGFuZCBwZXJjZW50Lm1pdG8uIEhlcmUgd2UgZGVjaWRlIHRvIHNjYWxlIGZvciBhbGwgZ2VuZXMgc28gdGhhdCBsYXRlciBvbiwgdGhlIGhlYXRtYXAgd2lsbCBiZSBnZW5lcmF0ZWQgdXNpbmcgdGhlIHNjYWxlZCBkYXRhLiAKYGBge3IgcmVzdWx0cz0naGlkZSd9CiNtYXliZSByZWdyZXNzIGNlbGwgY3ljbGUgaHR0cDovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2NlbGxfY3ljbGVfdmlnbmV0dGUuaHRtbAphbGwuZ2VuZXM8LXJvd25hbWVzKGdjcWwxKQojZ2NxbDEgPC0gU2NhbGVEYXRhKGdjcWwxLGZlYXR1cmVzID0gYWxsLmdlbmVzLCB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSkKZ2NxbDEgPC0gU2NhbGVEYXRhKGdjcWwxKQojb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpciwiMl9zY2FsZWRfYWxsZ2VuZXNfdW5pbXB1dGVkLnJkcyIpCiNzYXZlUkRTKGdjcWwxLCBmaWxlID0gb3V0cHV0LmZpbGVwYXRoKQpgYGAKCgpXZSBwZXJmb3JtIHRoZSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIG9uIHRoZSB2YXJpYWJsZSBnZW5lcyB0byBkZWNpZGUgdGhlIG51bWJlciBvZiBQQ3MgdG8gdXNlIGZvciBjbHVzdGVyaW5nLgpgYGB7cn0KZ2NxbDEgPC0gUnVuUENBKG9iamVjdCA9IGdjcWwxLCBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoZ2NxbDEpLCBucGNzPTIwMCx2ZXJib3NlPUYpCmBgYAoKClRvIHNlZSBpZiB0aGVyZSBhcmUgUENzIHRoYXQgYXJlIHN1YmplY3Qgc3BlY2lmaWMsIHRoZSAyRCBQQ0EgcGxvdHMgdXNpbmcgdGhlIHRvcCAzIFBDcyBhcmUgYXMgZm9sbG93cyBmb3IgdGhlIHVuLWltcHV0ZWQgZGF0YS4gCmBgYHtyIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTE1LGNhY2hlPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQojcGFyKG1mcm93PWMoMiwyKSkKZmlnLjE8LURpbVBsb3Qob2JqZWN0ID0gZ2NxbDEsIGRpbXMgPSBjKDEsMiksIHB0LnNpemU9MC41KQpmaWcuMjwtRGltUGxvdChvYmplY3QgPSBnY3FsMSwgZGltcyA9IGMoMiwzKSwgcHQuc2l6ZT0wLjUpCmZpZy4zPC1EaW1QbG90KG9iamVjdCA9IGdjcWwxLCBkaW1zID0gYygxLDMpLCBwdC5zaXplPTAuNSkKZ3JpZC5hcnJhbmdlKGZpZy4xLGZpZy4yLGZpZy4zLG5jb2w9MSkKYGBgCgpXZSBkcmF3IHRoZSBoZWF0bWFwIG9mIHRoZSB0b3AgZ2VuZXMgZm9yIGVhY2ggUEMuCmBgYHtyIGZpZy53aWR0aD0xNSxoZWlnaHQ9ODAsY2FjaGU9RkFMU0V9CkRpbUhlYXRtYXAoZ2NxbDEsIGRpbXMgPSAxOjEwLCBjZWxscyA9IDUwMCwgYmFsYW5jZWQgPSBUUlVFKQpgYGAKYGBge3IgZmlnLndpZHRoPTE1LGhlaWdodD04MCxjYWNoZT1GQUxTRX0KRGltSGVhdG1hcChnY3FsMSwgZGltcyA9IDExOjIwLCBjZWxscyA9IDUwMCwgYmFsYW5jZWQgPSBUUlVFKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xNSxoZWlnaHQ9ODAsY2FjaGU9RkFMU0V9CkRpbUhlYXRtYXAoZ2NxbDEsIGRpbXMgPSAyMTozMCwgY2VsbHMgPSA1MDAsIGJhbGFuY2VkID0gVFJVRSkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTUsaGVpZ2h0PTgwLGNhY2hlPUZBTFNFfQpEaW1IZWF0bWFwKGdjcWwxLCBkaW1zID0gMzE6NDAsIGNlbGxzID0gNTAwLCBiYWxhbmNlZCA9IFRSVUUpCmBgYAoKYGBge3IgZmlnLndpZHRoPTE1LGhlaWdodD04MCxjYWNoZT1GQUxTRX0KRGltSGVhdG1hcChnY3FsMSwgZGltcyA9IDQxOjUwLCBjZWxscyA9IDUwMCwgYmFsYW5jZWQgPSBUUlVFKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xNSxoZWlnaHQ9ODAsY2FjaGU9RkFMU0V9CkRpbUhlYXRtYXAoZ2NxbDEsIGRpbXMgPSA1MTo2MCwgY2VsbHMgPSA1MDAsIGJhbGFuY2VkID0gVFJVRSkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTUsaGVpZ2h0PTgwLGNhY2hlPUZBTFNFfQpEaW1IZWF0bWFwKGdjcWwxLCBkaW1zID0gNjE6NzAsIGNlbGxzID0gNTAwLCBiYWxhbmNlZCA9IFRSVUUpCmBgYAoKYGBge3IgZmlnLndpZHRoPTE1LGhlaWdodD04MCxjYWNoZT1GQUxTRX0KRGltSGVhdG1hcChnY3FsMSwgZGltcyA9IDcxOjgwLCBjZWxscyA9IDUwMCwgYmFsYW5jZWQgPSBUUlVFKQpgYGAKCldlIHVzZSB0aGUgZm9sbG93aW5nIEphY2tzdHJhdyBwbG90cyBhbmQgdGhlIEVsYm93cGxvdCB0byBkZWNpZGUgdGhlIG51bWJlciBvZiBQQ3MgdG8gdXNlIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLgpgYGB7ciBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD00MCxyZXN1bHRzPSdob2xkJ30KZ2NxbDEgPC0gSmFja1N0cmF3KG9iamVjdCA9IGdjcWwxLCBudW0ucmVwbGljYXRlID0gMTAwLCB2ZXJib3NlID0gVFJVRSxkaW1zPTIwMCkKZ2NxbDEgPC0gU2NvcmVKYWNrU3RyYXcoZ2NxbDEsIGRpbXMgPSAxOjIwMCkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTE2LGZpZy5oZWlnaHQ9OCxjYWNoZT1GQUxTRSxyZXN1bHRzPSdoaWRlJ30KSmFja1N0cmF3UGxvdChnY3FsMSwgZGltcyA9IDE6MTAwKQpgYGAKCmBgYHtyIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9OCxjYWNoZT1GQUxTRSxyZXN1bHRzPSdoaWRlJ30KRWxib3dQbG90KG9iamVjdCA9IGdjcWwxLG5kaW1zPTEwMCkKYGBgCgpgYGB7cn0KcGMubnVtPC0zNQpgYGAKCkJhc2VkIG9uIHRoZSBKYWNrU3RyYXcgcGxvdCBhbmQgdGhlIEVsYm93IHBsb3QsIHdlIGRlY2lkZWQgdG8gdXNlIHRoZSB0b3AgYHIgcGMubnVtYCBQQ3MgZm9yIHRoZSB1bmltcHV0ZWQgZGF0YS4KCmBgYHtyfQpzYXZlUkRTKGdjcWwxLCBmaWxlID0gZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoIjJfZGltcmVkdWN0aW9uX2RhdGFfIixsZW5ndGgoZ2NxbDFAYXNzYXlzJFJOQUB2YXIuZmVhdHVyZXMpLCJ2YXJnZW5lc19waXBlbGluZTEucmRzIixzZXA9IiIpKSkKIyB3ZSBzYXZlIHRoZSBmaWxlIGFzIHByZXByb2Nlc3NlZF9kYXRhX3NhdmVyLnJkcyB0byBwcmV2ZW50IGFueSBtaXN0YWtlcyBidXQgd2UgY2hhbmdlIHRoZSBmaWxlIG5hbWUgYmFjayB0byBwcmVwcm9jZXNzZWRfZGF0YS5yZHMgYmVmb3JlIHBlcmZvcm1pbmcgZG93bnN0cmVhbSBhbmFseXNpcy4KCmNhdCgiV2Ugc2F2ZSB0aGUgUiBwcm9qZWN0IGFzXG4iKQpjYXQoInVuaW1wdXRlZCBkYXRhOlx0IixmaWxlLnBhdGgob3V0cHV0LmRpcixwYXN0ZSgiMl9kaW1yZWR1Y3Rpb25fZGF0YV8iLGxlbmd0aChnY3FsMUBhc3NheXMkUk5BQHZhci5mZWF0dXJlcyksInZhcmdlbmVzX3BpcGVsaW5lMS5yZHMiLHNlcD0iIikpLCJcbiIsc2VwPSIiKQpgYGAKCgpCYXNlZCBvbiB0aGUgSmFja3N0cmF3IHBsb3RzLCB3ZSBkZWNpZGVkIHRvIHVzZSB0aGUgYHIgcGMubnVtYCB0b3AgUENzIGZvciBjbHVzdGVyaW5nLiAKCiMjIERhdGEgdmlzdWFsaXphdGlvbgoKIyMjIFVNQVAKVG8gaWRlbnRpZnkgcG90ZW50aWFsIG91dGx5aW5nIGNlbGxzIGFuZCBzYW1wbGVzLCB3ZSBmaXJzdCBsYWJlbCB0aGUgdFNORSBwbG90IHVzaW5nIGRpc2Vhc2Vfc2FtcGxlbmFtZXMuCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyAyLiBnZW5lcmF0ZSB0aGUgVFNORSBwbG90IGxhYmVsbGVkIHNwZWNpZmljYWxseSBmb3IgZWFjaCBzYW1wbGUgc2VwYXJhdGVseSB0byBsb29rIGZvciBvdXRsaWVycy4KIyBzcnVuIC0tcHR5IC0teDExIC1wIHBpX2thbWluc2tpIC10IDEyOjAwOjAwIC0tbnRhc2tzPTEgLS1ub2Rlcz0xIC0tY3B1cy1wZXItdGFzaz0xIC0tbWVtPTYwMTUyIGJhc2gKI21vZHVsZSBsb2FkIEFwcHMvUi8zLjQuMS1nZW5lcmljCiNtb2R1bGUgbG9hZCBBcHBzL1IvMy40LjMtZ2VuZXJpYwoKZGF0YS5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsIjJfZGltcmVkdWN0aW9uX2RhdGFfMjAwMHZhcmdlbmVzX3BpcGVsaW5lMS5yZHMiKQpnY3FsMSA8LSByZWFkUkRTKGZpbGUgPSBkYXRhLmZpbGVwYXRoLCByZWZob29rID0gTlVMTCkKCiMgZ2VuZXJhdGUgdGhlIFRTTkUgcGxvdCBsYWJlbGVkIGJ5IGxhYmVsaW5nIHNhbXBsZXMgd2l0aCBkaWZmZXJlbnQgY29sb3JzIApzYW1wbGUxPC1GaW5kTmVpZ2hib3JzKGdjcWwxLGRpbXM9MTpwYy5udW0pCnNhbXBsZTE8LUZpbmRDbHVzdGVycyhzYW1wbGUxLHJlc29sdXRpb249MS4yKQpzYW1wbGUxPC1SdW5VTUFQKHNhbXBsZTEsZGltcz0xOnBjLm51bSkKI3NhbXBsZTE8LVJ1blVNQVAoc2FtcGxlMSxkaW1zPTE6cGMubnVtLHVtYXAubWV0aG9kPSJ1bWFwLWxlYXJuIikKCgpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTIwLGZpZy5jYXA9IlVNQVAgbGFiZWxlZCBieSBhKS4gY2VsbCBjbHVzdGVycywgYikuIHNhbXBsZSBuYW1lcyBhbmQgYykuIGRpc2Vhc2UgZm9yIHRoZSB1bmltcHV0ZWQgZGF0YS4iLGNhY2hlPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQpmaWcxPC1EaW1QbG90KHNhbXBsZTEscmVkdWN0aW9uPSJ1bWFwIikKZmlnMjwtRGltUGxvdChzYW1wbGUxLHJlZHVjdGlvbj0idW1hcCIsZ3JvdXAuYnk9ImRpc2Vhc2Uuc2FtcGxlbmFtZXMiKQpmaWczPC1EaW1QbG90KHNhbXBsZTEscmVkdWN0aW9uPSJ1bWFwIixncm91cC5ieT0iZGlzZWFzZSIpCmdyaWQuYXJyYW5nZShmaWcxLGZpZzIsZmlnMyxuY29sPTEpCmBgYAoKIyMjIHRTTkUgcGxvdAoKYGBge3J9CnNhbXBsZTEgPC0gUnVuVFNORShzYW1wbGUxLCBkaW1zPSAxOnBjLm51bSkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0yMCxmaWcuY2FwPSJ0U05FIHBsb3RzIGxhYmVsZWQgYnkgYSkuIGNlbGwgY2x1c3RlcnMsIGIpLiBzYW1wbGUgbmFtZXMgYW5kIGMpLiBkaXNlYXNlIGZvciB0aGUgdW5pbXB1dGVkIGRhdGEuIixjYWNoZT1GQUxTRSxyZXN1bHRzPSdoaWRlJ30KZmlnMTwtRGltUGxvdChzYW1wbGUxLHJlZHVjdGlvbj0idHNuZSIpCmZpZzI8LURpbVBsb3Qoc2FtcGxlMSxyZWR1Y3Rpb249InRzbmUiLGdyb3VwLmJ5PSJkaXNlYXNlLnNhbXBsZW5hbWVzIikKZmlnMzwtRGltUGxvdChzYW1wbGUxLHJlZHVjdGlvbj0idHNuZSIsZ3JvdXAuYnk9ImRpc2Vhc2UiKQpncmlkLmFycmFuZ2UoZmlnMSxmaWcyLGZpZzMsbmNvbD0xKQpgYGAKCiMjIyBkYXRhIHNhdmluZwpgYGB7cn0Kc2F2ZVJEUyhzYW1wbGUxLCBmaWxlID0gZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoIjJfdmlzdWFsaXphdGlvbl9kYXRhXyIsbGVuZ3RoKGdjcWwxQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzKSwidmFyZ2VuZXNfcGlwZWxpbmUxLnJkcyIsc2VwPSIiKSkpCmNhdCgiV2Ugc2F2ZWQgdGhlIHNldXJhdCBvYmplY3Qgd2l0aCBVTUFQIGFuZCB0U05FIGFzICIsZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoIjJfdmlzdWFsaXphdGlvbl9kYXRhXyIsbGVuZ3RoKHNhbXBsZTFAYXNzYXlzJFJOQUB2YXIuZmVhdHVyZXMpLCJ2YXJnZW5lc19waXBlbGluZTEucmRzIixzZXA9IiIpKSwiXG4iLHNlcD0iIikKYGBgCgoKIyMjIEludGVncmF0aXZlIEFuYWx5c2lzCgpgYGB7cn0KbXkuZGlzdGluY3QuY29sb3JzPC1jKCcjZTYxOTRiJywgJyMzY2I0NGInLCAnI2ZmZTExOScsICcjNDM2M2Q4JywgJyNmNTgyMzEnLCAnIzkxMWViNCcsICcjNDZmMGYwJywgJyNmMDMyZTYnLCAnI2JjZjYwYycsICcjZmFiZWJlJywgJyMwMDgwODAnLCAnI2U2YmVmZicsICcjOWE2MzI0JywgJyNmZmZhYzgnLCAnIzgwMDAwMCcsICcjYWFmZmMzJywgJyM4MDgwMDAnLCAnI2ZmZDhiMScsICcjMDAwMDc1JywgJyM4MDgwODAnLCAnI2ZmZmZmZicsICcjMDAwMDAwJykKYGBgCgpUbyBkZXNjcmliZSB0aGUgY2VsbCB0eXBlcyBjYXB0dXJlZCBpbiB0aGUgZGF0YSwgdGhlIGludGVncmF0ZWQgYW5hbHlzaXMgbWF5IHByb3ZpZGUgY2xlYW5lciByZXN1bHRzLiBUaGVyZWZvcmUsIHdlIGNvbmR1Y3QgdGhlIGludGVncmF0ZWQgYW5hbHlzaXMgb2YgYWxsIHRoZSBhc3RobWEgMTBYIGRhdGEgdXNpbmcgU2V1cmF0IFYzLjEgaW4gdGhpcyBub3RlIHRvIHJlZHVjZSB0aGUgc3ViamVjdCBlZmZlY3QgaW4gdGhlIGRhdGEgdmlzdWxpemF0aW9uIGFuZCB0aGUgdHJhamVjdG9yeSBhbmFseXNpcyBhdCBsZWFzdC4gCgojIGRhdGEgbG9hZGluZwpgYGB7ciBkYXRhX2xvYWRpbmcscmVzdWx0cz0naG9sZCd9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgMS4gbG9hZCBpbiB0aGUgc2luZ2xlciBvYmplY3QKZGF0YS5kaXI8LW91dHB1dC5kaXIKc2FtcGxlMTwtcmVhZFJEUyhmaWxlLnBhdGgoZGF0YS5kaXIsIjJfdmlzdWFsaXphdGlvbl9kYXRhXzIwMDB2YXJnZW5lc19waXBlbGluZTEucmRzIikscmVmaG9vayA9IE5VTEwpCgpzYW1wbGUxLnJhdzwtc2FtcGxlMQpgYGAKCiMgaW50ZWdyYXRpbmcgZGF0YQoKU3BsaXQgdGhlIHdob2xlIGRhdGFzZXQgYmFzZWQgb24gc3ViamVjdCBJRCAoZnJlc2ggYW5kIERTTU8gc2FtcGxlcyBmcm9tIHRoZSBzYW1lIHN1YmplY3QgYXJlIGNvbnNpZGVyZWQgYXMgb25lIHNhbXBsZSkuIEZpbmQgYW5jaG9ycyB0byBpbnRlZ3JhdGUgYWxsIHRoZSBkYXRhc2V0cyB0b2dldGhlci4KYGBge3J9CnNhbXBsZTEubGlzdDwtU3BsaXRPYmplY3Qoc2FtcGxlMS5yYXcsc3BsaXQuYnk9InNhbXBsZS5uYW1lcyIpCnNhbXBsZTEubGlzdDwtbGFwcGx5KFg9c2FtcGxlMS5saXN0LEZVTj1mdW5jdGlvbih4KXsKICB4PC1Ob3JtYWxpemVEYXRhKHgpCiAgeDwtRmluZFZhcmlhYmxlRmVhdHVyZXMoeCxzZWxlY3Rpb24ubWV0aG9kPSJ2c3QiLG5mZWF0dXJlcz0yMDAwKQp9KQp0ZW1wLmNlbGxudW08LXVubGlzdChsYXBwbHkoc2FtcGxlMS5saXN0LG5jb2wpKQojIGluY3JlYXNlIHRoZSBnbG9iYWwgbWF4U2l6ZQpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemU9IDQxOTQzMDQwMDApCiNzYW1wbGUxLmFuY2hvcnM8LUZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMoc2FtcGxlMS5saXN0W3RlbXAuY2VsbG51bT49OTJdLGsuZmlsdGVyPTkyLGRpbXM9MTo0MCx2ZXJib3NlPUZBTFNFKQpzYW1wbGUxLmFuY2hvcnM8LUZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMoc2FtcGxlMS5saXN0W3RlbXAuY2VsbG51bT49NDBdLGsuZmlsdGVyPTQwLGRpbXM9MTo0MCx2ZXJib3NlPUZBTFNFKQojc2FtcGxlMS5jb21iaW5lZDwtSW50ZWdyYXRlRGF0YShhbmNob3JzZXQ9c2FtcGxlMS5hbmNob3JzLGZlYXR1cmVzLnRvLmludGVncmF0ZT1yb3duYW1lcyhzYW1wbGUxLnJhd0Bhc3NheXMkUk5BQGNvdW50cyksZGltcz0xOjQwLHZlcmJvc2U9RkFMU0UpCnNhbXBsZTEuY29tYmluZWQ8LUludGVncmF0ZURhdGEoYW5jaG9yc2V0PXNhbXBsZTEuYW5jaG9ycyxkaW1zPTE6NDAsdmVyYm9zZT1GQUxTRSkKYGBgCgoKYGBge3IgZGF0YV9zYXZpbmd9CmRpci5jcmVhdGUoZmlsZS5wYXRoKG91dHB1dC5kaXIsImludGVncmF0ZWRfYW5hbHlzaXMiKSkKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpciwiaW50ZWdyYXRlZF9hbmFseXNpcyIsInNldXJhdF9vYmplY3RfaW50ZWdyYXRlZF9hbGxjZWxscy5yZHMiKQpzYXZlUkRTKHNhbXBsZTEuY29tYmluZWQsZmlsZT1vdXRwdXQuZmlsZXBhdGgpCmNhdCgiV2Ugc2F2ZWQgdGhlIGludGVncmF0ZWQgZGF0YSBmb3IgYWxsIGNlbGxzIGFzICIsb3V0cHV0LmZpbGVwYXRoLCJcbiIsc2VwPSIiKQoKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpciwiaW50ZWdyYXRlZF9hbmFseXNpcyIsInNldXJhdF9vYmplY3Rfb3JpZ2luYWxfYWxsY2VsbHMucmRzIikKc2F2ZVJEUyhzYW1wbGUxLGZpbGU9b3V0cHV0LmZpbGVwYXRoKQpjYXQoIldlIHNhdmVkIHRoZSBvcmlnaW5hbCBkYXRhIGZvciBhbGwgY2VsbHMgYXMgIixvdXRwdXQuZmlsZXBhdGgsIlxuIixzZXA9IiIpCmBgYAoKQ2x1c3RlciB0aGUgY2VsbHMgdXNpbmcgdGhlIGludGVncmF0ZWQgZGF0YS4KCmBgYHtyfQpEZWZhdWx0QXNzYXkoc2FtcGxlMS5jb21iaW5lZCkgPC0gImludGVncmF0ZWQiCiMgUnVuIHRoZSBzdGFuZGFyZCB3b3JrZmxvdyBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY2x1c3RlcmluZwpzYW1wbGUxLmNvbWJpbmVkIDwtIFNjYWxlRGF0YShzYW1wbGUxLmNvbWJpbmVkLCB2ZXJib3NlID0gRkFMU0UpCnNhbXBsZTEuY29tYmluZWQgPC0gUnVuUENBKHNhbXBsZTEuY29tYmluZWQsIG5wY3MgPSA0MCwgdmVyYm9zZSA9IEZBTFNFKQojIHQtU05FIGFuZCBDbHVzdGVyaW5nCnNhbXBsZTEuY29tYmluZWQgPC0gUnVuVU1BUChzYW1wbGUxLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6NDApCnNhbXBsZTEuY29tYmluZWQgPC0gRmluZE5laWdoYm9ycyhzYW1wbGUxLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6NDApCnNhbXBsZTEuY29tYmluZWQgPC0gRmluZENsdXN0ZXJzKHNhbXBsZTEuY29tYmluZWQsIHJlc29sdXRpb24gPSAxLjIpCmBgYAoKV2Ugc2F2ZSB0aGUgcmVzdWx0IGluIGEgcmRzIGZpbGUuCmBgYHtyfQpvdXRwdXQuc3ViZGlyPC1maWxlLnBhdGgob3V0cHV0LmRpciwiaW50ZWdyYXRlZF9hbmFseXNpcyIpCmlmKGZpbGUuZXhpc3RzKG91dHB1dC5zdWJkaXIpPT1GKXsKICBkaXIuY3JlYXRlKG91dHB1dC5zdWJkaXIpCn0KCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5zdWJkaXIsInNldXJhdF9vYmplY3RfaW50ZWdyYXRlZF9hbGxjZWxsc19jZWxsY2x1c3RlcmluZ19ub1NpbmdsZVIucmRzIikKc2F2ZVJEUyhzYW1wbGUxLmNvbWJpbmVkLGZpbGU9b3V0cHV0LmZpbGVwYXRoKQpjYXQoIldlIHNhdmVkIHRoZSBzZXVyYXQgb2JqZWN0IG9mIHRoZSBpbnRlZ3JhdGVkIGRhdGEgd2l0aCBjZWxsIGNsdXN0ZXJpbmcgcmVzdWx0cyBvbmx5IGFzICIsb3V0cHV0LmZpbGVwYXRoLCJcbiIsc2VwPSIiKQpgYGAKCgojIFZpc3VhbGl6aW5nIGludGVncmF0ZWQgZGF0YQoKYGBge3IsZmlnLndpZHRoPTgsZmlnLmhlaWdodD0yMH0KbGlicmFyeShjb3dwbG90KQpwMSA8LSBEaW1QbG90KHNhbXBsZTEuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic2FtcGxlLm5hbWVzIikKcDIgPC0gRGltUGxvdChzYW1wbGUxLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSkKI3AzPC1EaW1QbG90KHNhbXBsZTEuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnk9InNpbmdsZXIuaHBjYS5jbHVzdGVyLm1lcmdlZCIsIGxhYmVsID0gVFJVRSxjb2xzPW15LmRpc3RpbmN0LmNvbG9ycykKI3A0PC1EaW1QbG90KHNhbXBsZTEuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnk9InNpbmdsZXIuaHBjYS5jbHVzdGVyIiwgbGFiZWwgPSBUUlVFLGNvbHM9bXkuZGlzdGluY3QuY29sb3JzKQpwNTwtRGltUGxvdChzYW1wbGUxLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5PSJkaXNlYXNlIiwgbGFiZWwgPSBUUlVFLGNvbHM9bXkuZGlzdGluY3QuY29sb3JzKQojcGxvdF9ncmlkKHAyLCBwMSwgcDMscDQscDUsbmNvbD0xKQpwbG90X2dyaWQocDIsIHAxLHA1LG5jb2w9MSkKYGBgCgojIEludGVncmF0ZSB3aXRoIGV4aXN0aW5nIGRhdGEKCldlIG9idGFpbmVkIGFub3RoZXIgUEJNQyAxMFggZGF0YSBhbm5vdGF0ZWQgaW4gS2FtaW5za2kgbGFiIHNvIHRoYXQgd2UgY2FuIGludGVncmF0ZSB0aGlzIGRhdGFzZXQgd2l0aCB0aGUgYW5ub3RhdGVkIGRhdGEgdG8gYW5ub3RhdGUgdGhlIGNlbGxzIGluIHRoaXMgZGF0YXNldC4KClZpc3VhbGl6ZSB0aGUgaW50ZWdyYXRlZCBkYXRhIGJ5IHNwbGl0dGluZyBpdCB0byBkaWZmZXJlbnQgY2x1c3RlcnMuCgpgYGB7cixmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01MH0KZmlnLmxpc3Q8LWxpc3QoKQpjZWxsdHlwZS5uYW1lczwtYXMubnVtZXJpYyh1bmlxdWUoYXMuY2hhcmFjdGVyKElkZW50cyhzYW1wbGUxLmNvbWJpbmVkKSkpKQpjZWxsdHlwZS5uYW1lczwtc29ydChjZWxsdHlwZS5uYW1lcyxkZWNyZWFzaW5nPUYpCmZvcihpIGluIDE6bGVuZ3RoKGNlbGx0eXBlLm5hbWVzKSl7CiAgdGVtcC5tYXA8LWFzLmNoYXJhY3RlcihJZGVudHMoc2FtcGxlMS5jb21iaW5lZCkpCiAgdGVtcC5tYXBbdGVtcC5tYXAhPWNlbGx0eXBlLm5hbWVzW2ldXT0ib3RoZXIiCiAgc2FtcGxlMS5jb21iaW5lZEBtZXRhLmRhdGEkdGVtcC5tYXA8LXRlbXAubWFwCiAgZmlnLmxpc3RbWzIqKGktMSkrMV1dPC1EaW1QbG90KHNhbXBsZTEuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBGQUxTRSxncm91cC5ieT0iaWRlbnQiKQogICNmaWcubGlzdFtbMippXV08LURpbVBsb3Qoc2FtcGxlMS5jb21iaW5lZCwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IEZBTFNFLGdyb3VwLmJ5PSJ0ZW1wLm1hcCIsY29scz1jKG15LmRpc3RpbmN0LmNvbG9yc1tpXSwiZ3JleSIpKQogIGZpZy5saXN0W1syKmldXTwtRGltUGxvdChzYW1wbGUxLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gRkFMU0UsZ3JvdXAuYnk9InRlbXAubWFwIixjb2xzPWMoInJlZCIsImdyZXkiKSkKfQoKZG8uY2FsbChncmlkLmFycmFuZ2UsYyhmaWcubGlzdCxuY29sPTIpKQpgYGAKClZpc3VhbGl6ZSB0aGUgaW50ZWdyYXRlZCBkYXRhIGJ5IHNwbGl0dGluZyBpdCB0byBkaWZmZXJlbnQgc3ViamVjdHMgYW5kIGRpZmZlcmVudCBjZWxsIHR5cGUgYW5ub3RhdGlvbiAob2xkKS4KCmBgYHtyLGZpZy53aWR0aD0xNixmaWcuaGVpZ2h0PTI0fQpmaWcubGlzdDwtbGlzdCgpCmNlbGx0eXBlLm5hbWVzPC11bmlxdWUoYXMuY2hhcmFjdGVyKHNhbXBsZTEuY29tYmluZWRAbWV0YS5kYXRhJHNpbmdsZXIuaHBjYS5jbHVzdGVyKSkKY2VsbHR5cGUubmFtZXM8LXNvcnQoY2VsbHR5cGUubmFtZXMsZGVjcmVhc2luZz1UKQpmb3IoaSBpbiAxOmxlbmd0aChjZWxsdHlwZS5uYW1lcykpewogIHRlbXAubWFwPC1hcy5jaGFyYWN0ZXIoc2FtcGxlMS5jb21iaW5lZEBtZXRhLmRhdGEkc2luZ2xlci5ocGNhLmNsdXN0ZXIpCiAgdGVtcC5tYXBbdGVtcC5tYXAhPWNlbGx0eXBlLm5hbWVzW2ldXTwtInpvdGhlciIKICBzYW1wbGUxLmNvbWJpbmVkQG1ldGEuZGF0YSR0ZW1wLm1hcDwtdGVtcC5tYXAKICBmaWcubGlzdFtbaV1dPC1EaW1QbG90KHNhbXBsZTEuY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIixncm91cC5ieT0idGVtcC5tYXAiLGxhYmVsID0gRkFMU0UscHQuc2l6ZT0wLjUsY29scz1jKG15LmRpc3RpbmN0LmNvbG9yc1tpXSwiZ3JleSIpKQp9CmRvLmNhbGwoZ3JpZC5hcnJhbmdlLGMoZmlnLmxpc3QsbmNvbD0yKSkKYGBgCgoKCg==